Salome HOME
30d762efca12c6e33327a4efd41a3b2d190a1996
[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;
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 ( startFace != startFaces.end() )
1163   {
1164     theFace = *startFace;
1165     const int nbNodes = theFace->NbCornerNodes();
1166
1167     avoidSet.clear();
1168     avoidSet.insert(theFace);
1169
1170     NLink link( theFace->GetNode( 0 ), 0 );
1171     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1172     {
1173       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1174       linkIt_isNew = checkedLinks.insert( link );
1175       if ( !linkIt_isNew.second )
1176       {
1177         // link has already been checked and won't be encountered more
1178         // if the group (theFaces) is manifold
1179         checkedLinks.erase( linkIt_isNew.first );
1180       }
1181       else
1182       {
1183         facesNearLink.clear();
1184         nodeIndsOfFace.clear();
1185         while (( otherFace = FindFaceInSet( link.first, link.second,
1186                                             theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1187           if ( otherFace != theFace)
1188           {
1189             facesNearLink.push_back( otherFace );
1190             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1191             avoidSet.insert( otherFace );
1192           }
1193         if ( facesNearLink.size() > 1 )
1194         {
1195           // select a face most co-directed with theFace,
1196           // other faces won't be visited this time
1197           gp_XYZ NF, NOF;
1198           SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1199           double proj, maxProj = 0;
1200           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1201             SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1202             if (( proj = Abs( NF * NOF )) > maxProj ) {
1203               maxProj = proj;
1204               otherFace = facesNearLink[i];
1205               nodeInd1  = nodeIndsOfFace[i].first;
1206               nodeInd2  = nodeIndsOfFace[i].second;
1207             }
1208           }
1209         }
1210         else if ( facesNearLink.size() == 1 )
1211         {
1212           otherFace = facesNearLink[0];
1213         }
1214         if ( otherFace && otherFace != theFace)
1215         {
1216           // link must be reverse in otherFace if orientation ot otherFace
1217           // is same as that of theFace
1218           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1219           {
1220             // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1221             //      << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1222             nbReori += Reorient( otherFace );
1223           }
1224           startFaces.insert( otherFace );
1225           if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1226             theFaces.erase( otherFace );
1227         }
1228       }
1229       std::swap( link.first, link.second ); // reverse the link
1230     }
1231     startFaces.erase( startFace );
1232     startFace = startFaces.begin();
1233   }
1234   return nbReori;
1235 }
1236
1237 //=======================================================================
1238 //function : getBadRate
1239 //purpose  :
1240 //=======================================================================
1241
1242 static double getBadRate (const SMDS_MeshElement*               theElem,
1243                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1244 {
1245   SMESH::Controls::TSequenceOfXYZ P;
1246   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1247     return 1e100;
1248   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1249   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1250 }
1251
1252 //=======================================================================
1253 //function : QuadToTri
1254 //purpose  : Cut quadrangles into triangles.
1255 //           theCrit is used to select a diagonal to cut
1256 //=======================================================================
1257
1258 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1259                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1260 {
1261   myLastCreatedElems.Clear();
1262   myLastCreatedNodes.Clear();
1263
1264   MESSAGE( "::QuadToTri()" );
1265
1266   if ( !theCrit.get() )
1267     return false;
1268
1269   SMESHDS_Mesh * aMesh = GetMeshDS();
1270
1271   Handle(Geom_Surface) surface;
1272   SMESH_MesherHelper   helper( *GetMesh() );
1273
1274   TIDSortedElemSet::iterator itElem;
1275   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1276     const SMDS_MeshElement* elem = *itElem;
1277     if ( !elem || elem->GetType() != SMDSAbs_Face )
1278       continue;
1279     if ( elem->NbCornerNodes() != 4 )
1280       continue;
1281
1282     // retrieve element nodes
1283     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1284
1285     // compare two sets of possible triangles
1286     double aBadRate1, aBadRate2; // to what extent a set is bad
1287     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1288     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1289     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1290
1291     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1292     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1293     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1294
1295     int aShapeId = FindShape( elem );
1296     const SMDS_MeshElement* newElem1 = 0;
1297     const SMDS_MeshElement* newElem2 = 0;
1298
1299     if( !elem->IsQuadratic() ) {
1300
1301       // split liner quadrangle
1302       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1303       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1304       if ( aBadRate1 <= aBadRate2 ) {
1305         // tr1 + tr2 is better
1306         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1307         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1308       }
1309       else {
1310         // tr3 + tr4 is better
1311         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1312         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1313       }
1314     }
1315     else {
1316
1317       // split quadratic quadrangle
1318
1319       // get surface elem is on
1320       if ( aShapeId != helper.GetSubShapeID() ) {
1321         surface.Nullify();
1322         TopoDS_Shape shape;
1323         if ( aShapeId > 0 )
1324           shape = aMesh->IndexToShape( aShapeId );
1325         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1326           TopoDS_Face face = TopoDS::Face( shape );
1327           surface = BRep_Tool::Surface( face );
1328           if ( !surface.IsNull() )
1329             helper.SetSubShape( shape );
1330         }
1331       }
1332       // find middle point for (0,1,2,3)
1333       // and create a node in this point;
1334       const SMDS_MeshNode* newN = 0;
1335       if ( aNodes.size() == 9 )
1336       {
1337         // SMDSEntity_BiQuad_Quadrangle
1338         newN = aNodes.back();
1339       }
1340       else
1341       {
1342         gp_XYZ p( 0,0,0 );
1343         if ( surface.IsNull() )
1344         {
1345           for ( int i = 0; i < 4; i++ )
1346             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1347           p /= 4;
1348         }
1349         else
1350         {
1351           const SMDS_MeshNode* inFaceNode = 0;
1352           if ( helper.GetNodeUVneedInFaceNode() )
1353             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1354               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1355                 inFaceNode = aNodes[ i ];
1356
1357           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1358           gp_XY uv( 0,0 );
1359           for ( int i = 0; i < 4; i++ )
1360             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1361           uv /= 4.;
1362           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1363         }
1364         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1365         myLastCreatedNodes.Append(newN);
1366       }
1367       // create a new element
1368       if ( aBadRate1 <= aBadRate2 ) {
1369         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1370                                   aNodes[6], aNodes[7], newN );
1371         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1372                                   newN,      aNodes[4], aNodes[5] );
1373       }
1374       else {
1375         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1376                                   aNodes[7], aNodes[4], newN );
1377         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1378                                   newN,      aNodes[5], aNodes[6] );
1379       }
1380     } // quadratic case
1381
1382     // care of a new element
1383
1384     myLastCreatedElems.Append(newElem1);
1385     myLastCreatedElems.Append(newElem2);
1386     AddToSameGroups( newElem1, elem, aMesh );
1387     AddToSameGroups( newElem2, elem, aMesh );
1388
1389     // put a new triangle on the same shape
1390     if ( aShapeId )
1391       {
1392         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1393         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1394       }
1395     aMesh->RemoveElement( elem );
1396   }
1397   return true;
1398 }
1399
1400 //=======================================================================
1401 //function : BestSplit
1402 //purpose  : Find better diagonal for cutting.
1403 //=======================================================================
1404
1405 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1406                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1407 {
1408   myLastCreatedElems.Clear();
1409   myLastCreatedNodes.Clear();
1410
1411   if (!theCrit.get())
1412     return -1;
1413
1414   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1415     return -1;
1416
1417   if( theQuad->NbNodes()==4 ||
1418       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1419
1420     // retrieve element nodes
1421     const SMDS_MeshNode* aNodes [4];
1422     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1423     int i = 0;
1424     //while (itN->more())
1425     while (i<4) {
1426       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1427     }
1428     // compare two sets of possible triangles
1429     double aBadRate1, aBadRate2; // to what extent a set is bad
1430     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1431     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1432     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433
1434     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1435     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1436     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1440       return 1; // diagonal 1-3
1441
1442     return 2; // diagonal 2-4
1443   }
1444   return -1;
1445 }
1446
1447 namespace
1448 {
1449   // Methods of splitting volumes into tetra
1450
1451   const int theHexTo5_1[5*4+1] =
1452     {
1453       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1454     };
1455   const int theHexTo5_2[5*4+1] =
1456     {
1457       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1458     };
1459   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1460
1461   const int theHexTo6_1[6*4+1] =
1462     {
1463       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
1464     };
1465   const int theHexTo6_2[6*4+1] =
1466     {
1467       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
1468     };
1469   const int theHexTo6_3[6*4+1] =
1470     {
1471       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
1472     };
1473   const int theHexTo6_4[6*4+1] =
1474     {
1475       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
1476     };
1477   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1478
1479   const int thePyraTo2_1[2*4+1] =
1480     {
1481       0, 1, 2, 4,    0, 2, 3, 4,   -1
1482     };
1483   const int thePyraTo2_2[2*4+1] =
1484     {
1485       1, 2, 3, 4,    1, 3, 0, 4,   -1
1486     };
1487   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1488
1489   const int thePentaTo3_1[3*4+1] =
1490     {
1491       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1492     };
1493   const int thePentaTo3_2[3*4+1] =
1494     {
1495       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1496     };
1497   const int thePentaTo3_3[3*4+1] =
1498     {
1499       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1500     };
1501   const int thePentaTo3_4[3*4+1] =
1502     {
1503       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1504     };
1505   const int thePentaTo3_5[3*4+1] =
1506     {
1507       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1508     };
1509   const int thePentaTo3_6[3*4+1] =
1510     {
1511       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1512     };
1513   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1514                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1515
1516   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1517   {
1518     int _n1, _n2, _n3;
1519     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1520     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1521     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1522   };
1523   struct TSplitMethod
1524   {
1525     int        _nbTetra;
1526     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1527     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1528     bool       _ownConn;      //!< to delete _connectivity in destructor
1529     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1530
1531     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1532       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1533     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1534     bool hasFacet( const TTriangleFacet& facet ) const
1535     {
1536       const int* tetConn = _connectivity;
1537       for ( ; tetConn[0] >= 0; tetConn += 4 )
1538         if (( facet.contains( tetConn[0] ) +
1539               facet.contains( tetConn[1] ) +
1540               facet.contains( tetConn[2] ) +
1541               facet.contains( tetConn[3] )) == 3 )
1542           return true;
1543       return false;
1544     }
1545   };
1546
1547   //=======================================================================
1548   /*!
1549    * \brief return TSplitMethod for the given element
1550    */
1551   //=======================================================================
1552
1553   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1554   {
1555     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1556
1557     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1558     // an edge and a face barycenter; tertaherdons are based on triangles and
1559     // a volume barycenter
1560     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1561
1562     // Find out how adjacent volumes are split
1563
1564     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1565     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1566     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1567     {
1568       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1569       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1570       if ( nbNodes < 4 ) continue;
1571
1572       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1573       const int* nInd = vol.GetFaceNodesIndices( iF );
1574       if ( nbNodes == 4 )
1575       {
1576         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1577         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1578         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1579         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1580       }
1581       else
1582       {
1583         int iCom = 0; // common node of triangle faces to split into
1584         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1585         {
1586           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1587                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1588                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1589           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1590                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1591                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1592           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1593           {
1594             triaSplits.push_back( t012 );
1595             triaSplits.push_back( t023 );
1596             break;
1597           }
1598         }
1599       }
1600       if ( !triaSplits.empty() )
1601         hasAdjacentSplits = true;
1602     }
1603
1604     // Among variants of split method select one compliant with adjacent volumes
1605
1606     TSplitMethod method;
1607     if ( !vol.Element()->IsPoly() && !is24TetMode )
1608     {
1609       int nbVariants = 2, nbTet = 0;
1610       const int** connVariants = 0;
1611       switch ( vol.Element()->GetEntityType() )
1612       {
1613       case SMDSEntity_Hexa:
1614       case SMDSEntity_Quad_Hexa:
1615       case SMDSEntity_TriQuad_Hexa:
1616         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1617           connVariants = theHexTo5, nbTet = 5;
1618         else
1619           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1620         break;
1621       case SMDSEntity_Pyramid:
1622       case SMDSEntity_Quad_Pyramid:
1623         connVariants = thePyraTo2;  nbTet = 2;
1624         break;
1625       case SMDSEntity_Penta:
1626       case SMDSEntity_Quad_Penta:
1627         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1628         break;
1629       default:
1630         nbVariants = 0;
1631       }
1632       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1633       {
1634         // check method compliancy with adjacent tetras,
1635         // all found splits must be among facets of tetras described by this method
1636         method = TSplitMethod( nbTet, connVariants[variant] );
1637         if ( hasAdjacentSplits && method._nbTetra > 0 )
1638         {
1639           bool facetCreated = true;
1640           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1641           {
1642             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1643             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1644               facetCreated = method.hasFacet( *facet );
1645           }
1646           if ( !facetCreated )
1647             method = TSplitMethod(0); // incompatible method
1648         }
1649       }
1650     }
1651     if ( method._nbTetra < 1 )
1652     {
1653       // No standard method is applicable, use a generic solution:
1654       // each facet of a volume is split into triangles and
1655       // each of triangles and a volume barycenter form a tetrahedron.
1656
1657       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1658
1659       int* connectivity = new int[ maxTetConnSize + 1 ];
1660       method._connectivity = connectivity;
1661       method._ownConn = true;
1662       method._baryNode = !isHex27; // to create central node or not
1663
1664       int connSize = 0;
1665       int baryCenInd = vol.NbNodes() - int( isHex27 );
1666       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1667       {
1668         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1669         const int*   nInd = vol.GetFaceNodesIndices( iF );
1670         // find common node of triangle facets of tetra to create
1671         int iCommon = 0; // index in linear numeration
1672         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1673         if ( !triaSplits.empty() )
1674         {
1675           // by found facets
1676           const TTriangleFacet* facet = &triaSplits.front();
1677           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1678             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1679                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1680               break;
1681         }
1682         else if ( nbNodes > 3 && !is24TetMode )
1683         {
1684           // find the best method of splitting into triangles by aspect ratio
1685           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1686           map< double, int > badness2iCommon;
1687           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1688           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1689           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1690           {
1691             double badness = 0;
1692             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1693             {
1694               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1695                                       nodes[ iQ*((iLast-1)%nbNodes)],
1696                                       nodes[ iQ*((iLast  )%nbNodes)]);
1697               badness += getBadRate( &tria, aspectRatio );
1698             }
1699             badness2iCommon.insert( make_pair( badness, iCommon ));
1700           }
1701           // use iCommon with lowest badness
1702           iCommon = badness2iCommon.begin()->second;
1703         }
1704         if ( iCommon >= nbNodes )
1705           iCommon = 0; // something wrong
1706
1707         // fill connectivity of tetrahedra based on a current face
1708         int nbTet = nbNodes - 2;
1709         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1710         {
1711           int faceBaryCenInd;
1712           if ( isHex27 )
1713           {
1714             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1715             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1716           }
1717           else
1718           {
1719             method._faceBaryNode[ iF ] = 0;
1720             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1721           }
1722           nbTet = nbNodes;
1723           for ( int i = 0; i < nbTet; ++i )
1724           {
1725             int i1 = i, i2 = (i+1) % nbNodes;
1726             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1727             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1728             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1729             connectivity[ connSize++ ] = faceBaryCenInd;
1730             connectivity[ connSize++ ] = baryCenInd;
1731           }
1732         }
1733         else
1734         {
1735           for ( int i = 0; i < nbTet; ++i )
1736           {
1737             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1738             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1739             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1740             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1741             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1742             connectivity[ connSize++ ] = baryCenInd;
1743           }
1744         }
1745         method._nbTetra += nbTet;
1746
1747       } // loop on volume faces
1748
1749       connectivity[ connSize++ ] = -1;
1750
1751     } // end of generic solution
1752
1753     return method;
1754   }
1755   //================================================================================
1756   /*!
1757    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1758    */
1759   //================================================================================
1760
1761   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1762   {
1763     // find the tetrahedron including the three nodes of facet
1764     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1765     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1766     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1767     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1768     while ( volIt1->more() )
1769     {
1770       const SMDS_MeshElement* v = volIt1->next();
1771       SMDSAbs_EntityType type = v->GetEntityType();
1772       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1773         continue;
1774       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1775         continue; // medium node not allowed
1776       const int ind2 = v->GetNodeIndex( n2 );
1777       if ( ind2 < 0 || 3 < ind2 )
1778         continue;
1779       const int ind3 = v->GetNodeIndex( n3 );
1780       if ( ind3 < 0 || 3 < ind3 )
1781         continue;
1782       return true;
1783     }
1784     return false;
1785   }
1786
1787   //=======================================================================
1788   /*!
1789    * \brief A key of a face of volume
1790    */
1791   //=======================================================================
1792
1793   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1794   {
1795     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1796     {
1797       TIDSortedNodeSet sortedNodes;
1798       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1799       int nbNodes = vol.NbFaceNodes( iF );
1800       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1801       for ( int i = 0; i < nbNodes; i += iQ )
1802         sortedNodes.insert( fNodes[i] );
1803       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1804       first.first   = (*(n++))->GetID();
1805       first.second  = (*(n++))->GetID();
1806       second.first  = (*(n++))->GetID();
1807       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1808     }
1809   };
1810 } // namespace
1811
1812 //=======================================================================
1813 //function : SplitVolumesIntoTetra
1814 //purpose  : Split volume elements into tetrahedra.
1815 //=======================================================================
1816
1817 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1818                                               const int                theMethodFlags)
1819 {
1820   // std-like iterator on coordinates of nodes of mesh element
1821   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1822   NXyzIterator xyzEnd;
1823
1824   SMDS_VolumeTool    volTool;
1825   SMESH_MesherHelper helper( *GetMesh());
1826
1827   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1828   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1829
1830   SMESH_SequenceOfElemPtr newNodes, newElems;
1831
1832   // map face of volume to it's baricenrtic node
1833   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1834   double bc[3];
1835
1836   TIDSortedElemSet::const_iterator elem = theElems.begin();
1837   for ( ; elem != theElems.end(); ++elem )
1838   {
1839     if ( (*elem)->GetType() != SMDSAbs_Volume )
1840       continue;
1841     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1842     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1843       continue;
1844
1845     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1846
1847     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1848     if ( splitMethod._nbTetra < 1 ) continue;
1849
1850     // find submesh to add new tetras to
1851     if ( !subMesh || !subMesh->Contains( *elem ))
1852     {
1853       int shapeID = FindShape( *elem );
1854       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1855       subMesh = GetMeshDS()->MeshElements( shapeID );
1856     }
1857     int iQ;
1858     if ( (*elem)->IsQuadratic() )
1859     {
1860       iQ = 2;
1861       // add quadratic links to the helper
1862       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1863       {
1864         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1865         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1866         for ( int iN = 0; iN < nbN; iN += iQ )
1867           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1868       }
1869       helper.SetIsQuadratic( true );
1870     }
1871     else
1872     {
1873       iQ = 1;
1874       helper.SetIsQuadratic( false );
1875     }
1876     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1877     helper.SetElementsOnShape( true );
1878     if ( splitMethod._baryNode )
1879     {
1880       // make a node at barycenter
1881       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1882       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1883       nodes.push_back( gcNode );
1884       newNodes.Append( gcNode );
1885     }
1886     if ( !splitMethod._faceBaryNode.empty() )
1887     {
1888       // make or find baricentric nodes of faces
1889       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1890       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1891       {
1892         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1893           volFace2BaryNode.insert
1894           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1895         if ( !f_n->second )
1896         {
1897           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1898           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1899         }
1900         nodes.push_back( iF_n->second = f_n->second );
1901       }
1902     }
1903
1904     // make tetras
1905     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1906     const int* tetConn = splitMethod._connectivity;
1907     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1908       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1909                                                        nodes[ tetConn[1] ],
1910                                                        nodes[ tetConn[2] ],
1911                                                        nodes[ tetConn[3] ]));
1912
1913     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1914
1915     // Split faces on sides of the split volume
1916
1917     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1918     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1919     {
1920       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1921       if ( nbNodes < 4 ) continue;
1922
1923       // find an existing face
1924       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1925                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1926       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1927                                                                        /*noMedium=*/false))
1928       {
1929         // make triangles
1930         helper.SetElementsOnShape( false );
1931         vector< const SMDS_MeshElement* > triangles;
1932
1933         // find submesh to add new triangles in
1934         if ( !fSubMesh || !fSubMesh->Contains( face ))
1935         {
1936           int shapeID = FindShape( face );
1937           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1938         }
1939         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1940         if ( iF_n != splitMethod._faceBaryNode.end() )
1941         {
1942           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1943           {
1944             const SMDS_MeshNode* n1 = fNodes[iN];
1945             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1946             const SMDS_MeshNode *n3 = iF_n->second;
1947             if ( !volTool.IsFaceExternal( iF ))
1948               swap( n2, n3 );
1949             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1950
1951             if ( fSubMesh && n3->getshapeId() < 1 )
1952               fSubMesh->AddNode( n3 );
1953           }
1954         }
1955         else
1956         {
1957           // among possible triangles create ones discribed by split method
1958           const int* nInd = volTool.GetFaceNodesIndices( iF );
1959           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1960           int iCom = 0; // common node of triangle faces to split into
1961           list< TTriangleFacet > facets;
1962           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1963           {
1964             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1965                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1966                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1967             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1968                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1969                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1970             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1971             {
1972               facets.push_back( t012 );
1973               facets.push_back( t023 );
1974               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1975                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1976                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1977                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1978               break;
1979             }
1980           }
1981           list< TTriangleFacet >::iterator facet = facets.begin();
1982           for ( ; facet != facets.end(); ++facet )
1983           {
1984             if ( !volTool.IsFaceExternal( iF ))
1985               swap( facet->_n2, facet->_n3 );
1986             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1987                                                  volNodes[ facet->_n2 ],
1988                                                  volNodes[ facet->_n3 ]));
1989           }
1990         }
1991         for ( int i = 0; i < triangles.size(); ++i )
1992         {
1993           if ( !triangles[i] ) continue;
1994           if ( fSubMesh )
1995             fSubMesh->AddElement( triangles[i]);
1996           newElems.Append( triangles[i] );
1997         }
1998         ReplaceElemInGroups( face, triangles, GetMeshDS() );
1999         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2000       }
2001
2002     } // loop on volume faces to split them into triangles
2003
2004     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2005
2006     if ( geomType == SMDSEntity_TriQuad_Hexa )
2007     {
2008       // remove medium nodes that could become free
2009       for ( int i = 20; i < volTool.NbNodes(); ++i )
2010         if ( volNodes[i]->NbInverseElements() == 0 )
2011           GetMeshDS()->RemoveNode( volNodes[i] );
2012     }
2013   } // loop on volumes to split
2014
2015   myLastCreatedNodes = newNodes;
2016   myLastCreatedElems = newElems;
2017 }
2018
2019 //=======================================================================
2020 //function : AddToSameGroups
2021 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2022 //=======================================================================
2023
2024 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2025                                         const SMDS_MeshElement* elemInGroups,
2026                                         SMESHDS_Mesh *          aMesh)
2027 {
2028   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2029   if (!groups.empty()) {
2030     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2031     for ( ; grIt != groups.end(); grIt++ ) {
2032       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2033       if ( group && group->Contains( elemInGroups ))
2034         group->SMDSGroup().Add( elemToAdd );
2035     }
2036   }
2037 }
2038
2039
2040 //=======================================================================
2041 //function : RemoveElemFromGroups
2042 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2043 //=======================================================================
2044 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2045                                              SMESHDS_Mesh *          aMesh)
2046 {
2047   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2048   if (!groups.empty())
2049   {
2050     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2051     for (; GrIt != groups.end(); GrIt++)
2052     {
2053       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2054       if (!grp || grp->IsEmpty()) continue;
2055       grp->SMDSGroup().Remove(removeelem);
2056     }
2057   }
2058 }
2059
2060 //================================================================================
2061 /*!
2062  * \brief Replace elemToRm by elemToAdd in the all groups
2063  */
2064 //================================================================================
2065
2066 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2067                                             const SMDS_MeshElement* elemToAdd,
2068                                             SMESHDS_Mesh *          aMesh)
2069 {
2070   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2071   if (!groups.empty()) {
2072     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2073     for ( ; grIt != groups.end(); grIt++ ) {
2074       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2075       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2076         group->SMDSGroup().Add( elemToAdd );
2077     }
2078   }
2079 }
2080
2081 //================================================================================
2082 /*!
2083  * \brief Replace elemToRm by elemToAdd in the all groups
2084  */
2085 //================================================================================
2086
2087 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2088                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2089                                             SMESHDS_Mesh *                         aMesh)
2090 {
2091   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2092   if (!groups.empty())
2093   {
2094     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2095     for ( ; grIt != groups.end(); grIt++ ) {
2096       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2097       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2098         for ( int i = 0; i < elemToAdd.size(); ++i )
2099           group->SMDSGroup().Add( elemToAdd[ i ] );
2100     }
2101   }
2102 }
2103
2104 //=======================================================================
2105 //function : QuadToTri
2106 //purpose  : Cut quadrangles into triangles.
2107 //           theCrit is used to select a diagonal to cut
2108 //=======================================================================
2109
2110 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2111                                   const bool         the13Diag)
2112 {
2113   myLastCreatedElems.Clear();
2114   myLastCreatedNodes.Clear();
2115
2116   MESSAGE( "::QuadToTri()" );
2117
2118   SMESHDS_Mesh * aMesh = GetMeshDS();
2119
2120   Handle(Geom_Surface) surface;
2121   SMESH_MesherHelper   helper( *GetMesh() );
2122
2123   TIDSortedElemSet::iterator itElem;
2124   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2125     const SMDS_MeshElement* elem = *itElem;
2126     if ( !elem || elem->GetType() != SMDSAbs_Face )
2127       continue;
2128     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2129     if(!isquad) continue;
2130
2131     if(elem->NbNodes()==4) {
2132       // retrieve element nodes
2133       const SMDS_MeshNode* aNodes [4];
2134       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2135       int i = 0;
2136       while ( itN->more() )
2137         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2138
2139       int aShapeId = FindShape( elem );
2140       const SMDS_MeshElement* newElem1 = 0;
2141       const SMDS_MeshElement* newElem2 = 0;
2142       if ( the13Diag ) {
2143         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2144         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2145       }
2146       else {
2147         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2148         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2149       }
2150       myLastCreatedElems.Append(newElem1);
2151       myLastCreatedElems.Append(newElem2);
2152       // put a new triangle on the same shape and add to the same groups
2153       if ( aShapeId )
2154         {
2155           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2156           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2157         }
2158       AddToSameGroups( newElem1, elem, aMesh );
2159       AddToSameGroups( newElem2, elem, aMesh );
2160       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2161       aMesh->RemoveElement( elem );
2162     }
2163
2164     // Quadratic quadrangle
2165
2166     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2167
2168       // get surface elem is on
2169       int aShapeId = FindShape( elem );
2170       if ( aShapeId != helper.GetSubShapeID() ) {
2171         surface.Nullify();
2172         TopoDS_Shape shape;
2173         if ( aShapeId > 0 )
2174           shape = aMesh->IndexToShape( aShapeId );
2175         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2176           TopoDS_Face face = TopoDS::Face( shape );
2177           surface = BRep_Tool::Surface( face );
2178           if ( !surface.IsNull() )
2179             helper.SetSubShape( shape );
2180         }
2181       }
2182
2183       const SMDS_MeshNode* aNodes [8];
2184       const SMDS_MeshNode* inFaceNode = 0;
2185       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2186       int i = 0;
2187       while ( itN->more() ) {
2188         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2189         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2190              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2191         {
2192           inFaceNode = aNodes[ i-1 ];
2193         }
2194       }
2195
2196       // find middle point for (0,1,2,3)
2197       // and create a node in this point;
2198       gp_XYZ p( 0,0,0 );
2199       if ( surface.IsNull() ) {
2200         for(i=0; i<4; i++)
2201           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2202         p /= 4;
2203       }
2204       else {
2205         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2206         gp_XY uv( 0,0 );
2207         for(i=0; i<4; i++)
2208           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2209         uv /= 4.;
2210         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2211       }
2212       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2213       myLastCreatedNodes.Append(newN);
2214
2215       // create a new element
2216       const SMDS_MeshElement* newElem1 = 0;
2217       const SMDS_MeshElement* newElem2 = 0;
2218       if ( the13Diag ) {
2219         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2220                                   aNodes[6], aNodes[7], newN );
2221         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2222                                   newN,      aNodes[4], aNodes[5] );
2223       }
2224       else {
2225         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2226                                   aNodes[7], aNodes[4], newN );
2227         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2228                                   newN,      aNodes[5], aNodes[6] );
2229       }
2230       myLastCreatedElems.Append(newElem1);
2231       myLastCreatedElems.Append(newElem2);
2232       // put a new triangle on the same shape and add to the same groups
2233       if ( aShapeId )
2234         {
2235           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2236           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2237         }
2238       AddToSameGroups( newElem1, elem, aMesh );
2239       AddToSameGroups( newElem2, elem, aMesh );
2240       aMesh->RemoveElement( elem );
2241     }
2242   }
2243
2244   return true;
2245 }
2246
2247 //=======================================================================
2248 //function : getAngle
2249 //purpose  :
2250 //=======================================================================
2251
2252 double getAngle(const SMDS_MeshElement * tr1,
2253                 const SMDS_MeshElement * tr2,
2254                 const SMDS_MeshNode *    n1,
2255                 const SMDS_MeshNode *    n2)
2256 {
2257   double angle = 2. * M_PI; // bad angle
2258
2259   // get normals
2260   SMESH::Controls::TSequenceOfXYZ P1, P2;
2261   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2262        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2263     return angle;
2264   gp_Vec N1,N2;
2265   if(!tr1->IsQuadratic())
2266     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2267   else
2268     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2269   if ( N1.SquareMagnitude() <= gp::Resolution() )
2270     return angle;
2271   if(!tr2->IsQuadratic())
2272     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2273   else
2274     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2275   if ( N2.SquareMagnitude() <= gp::Resolution() )
2276     return angle;
2277
2278   // find the first diagonal node n1 in the triangles:
2279   // take in account a diagonal link orientation
2280   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2281   for ( int t = 0; t < 2; t++ ) {
2282     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2283     int i = 0, iDiag = -1;
2284     while ( it->more()) {
2285       const SMDS_MeshElement *n = it->next();
2286       if ( n == n1 || n == n2 ) {
2287         if ( iDiag < 0)
2288           iDiag = i;
2289         else {
2290           if ( i - iDiag == 1 )
2291             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2292           else
2293             nFirst[ t ] = n;
2294           break;
2295         }
2296       }
2297       i++;
2298     }
2299   }
2300   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2301     N2.Reverse();
2302
2303   angle = N1.Angle( N2 );
2304   //SCRUTE( angle );
2305   return angle;
2306 }
2307
2308 // =================================================
2309 // class generating a unique ID for a pair of nodes
2310 // and able to return nodes by that ID
2311 // =================================================
2312 class LinkID_Gen {
2313 public:
2314
2315   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2316     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2317   {}
2318
2319   long GetLinkID (const SMDS_MeshNode * n1,
2320                   const SMDS_MeshNode * n2) const
2321   {
2322     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2323   }
2324
2325   bool GetNodes (const long             theLinkID,
2326                  const SMDS_MeshNode* & theNode1,
2327                  const SMDS_MeshNode* & theNode2) const
2328   {
2329     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2330     if ( !theNode1 ) return false;
2331     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2332     if ( !theNode2 ) return false;
2333     return true;
2334   }
2335
2336 private:
2337   LinkID_Gen();
2338   const SMESHDS_Mesh* myMesh;
2339   long                myMaxID;
2340 };
2341
2342
2343 //=======================================================================
2344 //function : TriToQuad
2345 //purpose  : Fuse neighbour triangles into quadrangles.
2346 //           theCrit is used to select a neighbour to fuse with.
2347 //           theMaxAngle is a max angle between element normals at which
2348 //           fusion is still performed.
2349 //=======================================================================
2350
2351 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2352                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2353                                   const double                         theMaxAngle)
2354 {
2355   myLastCreatedElems.Clear();
2356   myLastCreatedNodes.Clear();
2357
2358   MESSAGE( "::TriToQuad()" );
2359
2360   if ( !theCrit.get() )
2361     return false;
2362
2363   SMESHDS_Mesh * aMesh = GetMeshDS();
2364
2365   // Prepare data for algo: build
2366   // 1. map of elements with their linkIDs
2367   // 2. map of linkIDs with their elements
2368
2369   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2370   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2371   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2372   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2373
2374   TIDSortedElemSet::iterator itElem;
2375   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2376     const SMDS_MeshElement* elem = *itElem;
2377     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2378     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2379     if(!IsTria) continue;
2380
2381     // retrieve element nodes
2382     const SMDS_MeshNode* aNodes [4];
2383     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2384     int i = 0;
2385     while ( i<3 )
2386       aNodes[ i++ ] = cast2Node( itN->next() );
2387     aNodes[ 3 ] = aNodes[ 0 ];
2388
2389     // fill maps
2390     for ( i = 0; i < 3; i++ ) {
2391       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2392       // check if elements sharing a link can be fused
2393       itLE = mapLi_listEl.find( link );
2394       if ( itLE != mapLi_listEl.end() ) {
2395         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2396           continue;
2397         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2398         //if ( FindShape( elem ) != FindShape( elem2 ))
2399         //  continue; // do not fuse triangles laying on different shapes
2400         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2401           continue; // avoid making badly shaped quads
2402         (*itLE).second.push_back( elem );
2403       }
2404       else {
2405         mapLi_listEl[ link ].push_back( elem );
2406       }
2407       mapEl_setLi [ elem ].insert( link );
2408     }
2409   }
2410   // Clean the maps from the links shared by a sole element, ie
2411   // links to which only one element is bound in mapLi_listEl
2412
2413   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2414     int nbElems = (*itLE).second.size();
2415     if ( nbElems < 2  ) {
2416       const SMDS_MeshElement* elem = (*itLE).second.front();
2417       SMESH_TLink link = (*itLE).first;
2418       mapEl_setLi[ elem ].erase( link );
2419       if ( mapEl_setLi[ elem ].empty() )
2420         mapEl_setLi.erase( elem );
2421     }
2422   }
2423
2424   // Algo: fuse triangles into quadrangles
2425
2426   while ( ! mapEl_setLi.empty() ) {
2427     // Look for the start element:
2428     // the element having the least nb of shared links
2429     const SMDS_MeshElement* startElem = 0;
2430     int minNbLinks = 4;
2431     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2432       int nbLinks = (*itEL).second.size();
2433       if ( nbLinks < minNbLinks ) {
2434         startElem = (*itEL).first;
2435         minNbLinks = nbLinks;
2436         if ( minNbLinks == 1 )
2437           break;
2438       }
2439     }
2440
2441     // search elements to fuse starting from startElem or links of elements
2442     // fused earlyer - startLinks
2443     list< SMESH_TLink > startLinks;
2444     while ( startElem || !startLinks.empty() ) {
2445       while ( !startElem && !startLinks.empty() ) {
2446         // Get an element to start, by a link
2447         SMESH_TLink linkId = startLinks.front();
2448         startLinks.pop_front();
2449         itLE = mapLi_listEl.find( linkId );
2450         if ( itLE != mapLi_listEl.end() ) {
2451           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2452           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2453           for ( ; itE != listElem.end() ; itE++ )
2454             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2455               startElem = (*itE);
2456           mapLi_listEl.erase( itLE );
2457         }
2458       }
2459
2460       if ( startElem ) {
2461         // Get candidates to be fused
2462         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2463         const SMESH_TLink *link12, *link13;
2464         startElem = 0;
2465         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2466         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2467         ASSERT( !setLi.empty() );
2468         set< SMESH_TLink >::iterator itLi;
2469         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2470         {
2471           const SMESH_TLink & link = (*itLi);
2472           itLE = mapLi_listEl.find( link );
2473           if ( itLE == mapLi_listEl.end() )
2474             continue;
2475
2476           const SMDS_MeshElement* elem = (*itLE).second.front();
2477           if ( elem == tr1 )
2478             elem = (*itLE).second.back();
2479           mapLi_listEl.erase( itLE );
2480           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2481             continue;
2482           if ( tr2 ) {
2483             tr3 = elem;
2484             link13 = &link;
2485           }
2486           else {
2487             tr2 = elem;
2488             link12 = &link;
2489           }
2490
2491           // add other links of elem to list of links to re-start from
2492           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2493           set< SMESH_TLink >::iterator it;
2494           for ( it = links.begin(); it != links.end(); it++ ) {
2495             const SMESH_TLink& link2 = (*it);
2496             if ( link2 != link )
2497               startLinks.push_back( link2 );
2498           }
2499         }
2500
2501         // Get nodes of possible quadrangles
2502         const SMDS_MeshNode *n12 [4], *n13 [4];
2503         bool Ok12 = false, Ok13 = false;
2504         const SMDS_MeshNode *linkNode1, *linkNode2;
2505         if(tr2) {
2506           linkNode1 = link12->first;
2507           linkNode2 = link12->second;
2508           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2509             Ok12 = true;
2510         }
2511         if(tr3) {
2512           linkNode1 = link13->first;
2513           linkNode2 = link13->second;
2514           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2515             Ok13 = true;
2516         }
2517
2518         // Choose a pair to fuse
2519         if ( Ok12 && Ok13 ) {
2520           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2521           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2522           double aBadRate12 = getBadRate( &quad12, theCrit );
2523           double aBadRate13 = getBadRate( &quad13, theCrit );
2524           if (  aBadRate13 < aBadRate12 )
2525             Ok12 = false;
2526           else
2527             Ok13 = false;
2528         }
2529
2530         // Make quadrangles
2531         // and remove fused elems and removed links from the maps
2532         mapEl_setLi.erase( tr1 );
2533         if ( Ok12 ) {
2534           mapEl_setLi.erase( tr2 );
2535           mapLi_listEl.erase( *link12 );
2536           if(tr1->NbNodes()==3) {
2537             const SMDS_MeshElement* newElem = 0;
2538             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2539             myLastCreatedElems.Append(newElem);
2540             AddToSameGroups( newElem, tr1, aMesh );
2541             int aShapeId = tr1->getshapeId();
2542             if ( aShapeId )
2543               {
2544                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2545               }
2546             aMesh->RemoveElement( tr1 );
2547             aMesh->RemoveElement( tr2 );
2548           }
2549           else {
2550             const SMDS_MeshNode* N1 [6];
2551             const SMDS_MeshNode* N2 [6];
2552             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2553             // now we receive following N1 and N2 (using numeration as above image)
2554             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2555             // i.e. first nodes from both arrays determ new diagonal
2556             const SMDS_MeshNode* aNodes[8];
2557             aNodes[0] = N1[0];
2558             aNodes[1] = N1[1];
2559             aNodes[2] = N2[0];
2560             aNodes[3] = N2[1];
2561             aNodes[4] = N1[3];
2562             aNodes[5] = N2[5];
2563             aNodes[6] = N2[3];
2564             aNodes[7] = N1[5];
2565             const SMDS_MeshElement* newElem = 0;
2566             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2567                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2568             myLastCreatedElems.Append(newElem);
2569             AddToSameGroups( newElem, tr1, aMesh );
2570             int aShapeId = tr1->getshapeId();
2571             if ( aShapeId )
2572               {
2573                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2574               }
2575             aMesh->RemoveElement( tr1 );
2576             aMesh->RemoveElement( tr2 );
2577             // remove middle node (9)
2578             GetMeshDS()->RemoveNode( N1[4] );
2579           }
2580         }
2581         else if ( Ok13 ) {
2582           mapEl_setLi.erase( tr3 );
2583           mapLi_listEl.erase( *link13 );
2584           if(tr1->NbNodes()==3) {
2585             const SMDS_MeshElement* newElem = 0;
2586             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2587             myLastCreatedElems.Append(newElem);
2588             AddToSameGroups( newElem, tr1, aMesh );
2589             int aShapeId = tr1->getshapeId();
2590             if ( aShapeId )
2591               {
2592                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2593               }
2594             aMesh->RemoveElement( tr1 );
2595             aMesh->RemoveElement( tr3 );
2596           }
2597           else {
2598             const SMDS_MeshNode* N1 [6];
2599             const SMDS_MeshNode* N2 [6];
2600             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2601             // now we receive following N1 and N2 (using numeration as above image)
2602             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2603             // i.e. first nodes from both arrays determ new diagonal
2604             const SMDS_MeshNode* aNodes[8];
2605             aNodes[0] = N1[0];
2606             aNodes[1] = N1[1];
2607             aNodes[2] = N2[0];
2608             aNodes[3] = N2[1];
2609             aNodes[4] = N1[3];
2610             aNodes[5] = N2[5];
2611             aNodes[6] = N2[3];
2612             aNodes[7] = N1[5];
2613             const SMDS_MeshElement* newElem = 0;
2614             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2615                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2616             myLastCreatedElems.Append(newElem);
2617             AddToSameGroups( newElem, tr1, aMesh );
2618             int aShapeId = tr1->getshapeId();
2619             if ( aShapeId )
2620               {
2621                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2622               }
2623             aMesh->RemoveElement( tr1 );
2624             aMesh->RemoveElement( tr3 );
2625             // remove middle node (9)
2626             GetMeshDS()->RemoveNode( N1[4] );
2627           }
2628         }
2629
2630         // Next element to fuse: the rejected one
2631         if ( tr3 )
2632           startElem = Ok12 ? tr3 : tr2;
2633
2634       } // if ( startElem )
2635     } // while ( startElem || !startLinks.empty() )
2636   } // while ( ! mapEl_setLi.empty() )
2637
2638   return true;
2639 }
2640
2641
2642 /*#define DUMPSO(txt) \
2643 //  cout << txt << endl;
2644 //=============================================================================
2645 //
2646 //
2647 //
2648 //=============================================================================
2649 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2650 {
2651 if ( i1 == i2 )
2652 return;
2653 int tmp = idNodes[ i1 ];
2654 idNodes[ i1 ] = idNodes[ i2 ];
2655 idNodes[ i2 ] = tmp;
2656 gp_Pnt Ptmp = P[ i1 ];
2657 P[ i1 ] = P[ i2 ];
2658 P[ i2 ] = Ptmp;
2659 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2660 }
2661
2662 //=======================================================================
2663 //function : SortQuadNodes
2664 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2665 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2666 //           1 or 2 else 0.
2667 //=======================================================================
2668
2669 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2670 int               idNodes[] )
2671 {
2672   gp_Pnt P[4];
2673   int i;
2674   for ( i = 0; i < 4; i++ ) {
2675     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2676     if ( !n ) return 0;
2677     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2678   }
2679
2680   gp_Vec V1(P[0], P[1]);
2681   gp_Vec V2(P[0], P[2]);
2682   gp_Vec V3(P[0], P[3]);
2683
2684   gp_Vec Cross1 = V1 ^ V2;
2685   gp_Vec Cross2 = V2 ^ V3;
2686
2687   i = 0;
2688   if (Cross1.Dot(Cross2) < 0)
2689   {
2690     Cross1 = V2 ^ V1;
2691     Cross2 = V1 ^ V3;
2692
2693     if (Cross1.Dot(Cross2) < 0)
2694       i = 2;
2695     else
2696       i = 1;
2697     swap ( i, i + 1, idNodes, P );
2698
2699     //     for ( int ii = 0; ii < 4; ii++ ) {
2700     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2701     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2702     //     }
2703   }
2704   return i;
2705 }
2706
2707 //=======================================================================
2708 //function : SortHexaNodes
2709 //purpose  : Set 8 nodes of a hexahedron in a good order.
2710 //           Return success status
2711 //=======================================================================
2712
2713 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2714                                       int               idNodes[] )
2715 {
2716   gp_Pnt P[8];
2717   int i;
2718   DUMPSO( "INPUT: ========================================");
2719   for ( i = 0; i < 8; i++ ) {
2720     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2721     if ( !n ) return false;
2722     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2723     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2724   }
2725   DUMPSO( "========================================");
2726
2727
2728   set<int> faceNodes;  // ids of bottom face nodes, to be found
2729   set<int> checkedId1; // ids of tried 2-nd nodes
2730   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2731   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2732   int iMin, iLoop1 = 0;
2733
2734   // Loop to try the 2-nd nodes
2735
2736   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2737   {
2738     // Find not checked 2-nd node
2739     for ( i = 1; i < 8; i++ )
2740       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2741         int id1 = idNodes[i];
2742         swap ( 1, i, idNodes, P );
2743         checkedId1.insert ( id1 );
2744         break;
2745       }
2746
2747     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2748     // ie that all but meybe one (id3 which is on the same face) nodes
2749     // lay on the same side from the triangle plane.
2750
2751     bool manyInPlane = false; // more than 4 nodes lay in plane
2752     int iLoop2 = 0;
2753     while ( ++iLoop2 < 6 ) {
2754
2755       // get 1-2-3 plane coeffs
2756       Standard_Real A, B, C, D;
2757       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2758       if ( N.SquareMagnitude() > gp::Resolution() )
2759       {
2760         gp_Pln pln ( P[0], N );
2761         pln.Coefficients( A, B, C, D );
2762
2763         // find the node (iMin) closest to pln
2764         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2765         set<int> idInPln;
2766         for ( i = 3; i < 8; i++ ) {
2767           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2768           if ( fabs( dist[i] ) < minDist ) {
2769             minDist = fabs( dist[i] );
2770             iMin = i;
2771           }
2772           if ( fabs( dist[i] ) <= tol )
2773             idInPln.insert( idNodes[i] );
2774         }
2775
2776         // there should not be more than 4 nodes in bottom plane
2777         if ( idInPln.size() > 1 )
2778         {
2779           DUMPSO( "### idInPln.size() = " << idInPln.size());
2780           // idInPlane does not contain the first 3 nodes
2781           if ( manyInPlane || idInPln.size() == 5)
2782             return false; // all nodes in one plane
2783           manyInPlane = true;
2784
2785           // set the 1-st node to be not in plane
2786           for ( i = 3; i < 8; i++ ) {
2787             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2788               DUMPSO( "### Reset 0-th node");
2789               swap( 0, i, idNodes, P );
2790               break;
2791             }
2792           }
2793
2794           // reset to re-check second nodes
2795           leastDist = DBL_MAX;
2796           faceNodes.clear();
2797           checkedId1.clear();
2798           iLoop1 = 0;
2799           break; // from iLoop2;
2800         }
2801
2802         // check that the other 4 nodes are on the same side
2803         bool sameSide = true;
2804         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2805         for ( i = 3; sameSide && i < 8; i++ ) {
2806           if ( i != iMin )
2807             sameSide = ( isNeg == dist[i] <= 0.);
2808         }
2809
2810         // keep best solution
2811         if ( sameSide && minDist < leastDist ) {
2812           leastDist = minDist;
2813           faceNodes.clear();
2814           faceNodes.insert( idNodes[ 1 ] );
2815           faceNodes.insert( idNodes[ 2 ] );
2816           faceNodes.insert( idNodes[ iMin ] );
2817           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2818                   << " leastDist = " << leastDist);
2819           if ( leastDist <= DBL_MIN )
2820             break;
2821         }
2822       }
2823
2824       // set next 3-d node to check
2825       int iNext = 2 + iLoop2;
2826       if ( iNext < 8 ) {
2827         DUMPSO( "Try 2-nd");
2828         swap ( 2, iNext, idNodes, P );
2829       }
2830     } // while ( iLoop2 < 6 )
2831   } // iLoop1
2832
2833   if ( faceNodes.empty() ) return false;
2834
2835   // Put the faceNodes in proper places
2836   for ( i = 4; i < 8; i++ ) {
2837     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2838       // find a place to put
2839       int iTo = 1;
2840       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2841         iTo++;
2842       DUMPSO( "Set faceNodes");
2843       swap ( iTo, i, idNodes, P );
2844     }
2845   }
2846
2847
2848   // Set nodes of the found bottom face in good order
2849   DUMPSO( " Found bottom face: ");
2850   i = SortQuadNodes( theMesh, idNodes );
2851   if ( i ) {
2852     gp_Pnt Ptmp = P[ i ];
2853     P[ i ] = P[ i+1 ];
2854     P[ i+1 ] = Ptmp;
2855   }
2856   //   else
2857   //     for ( int ii = 0; ii < 4; ii++ ) {
2858   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2859   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2860   //    }
2861
2862   // Gravity center of the top and bottom faces
2863   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2864   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2865
2866   // Get direction from the bottom to the top face
2867   gp_Vec upDir ( aGCb, aGCt );
2868   Standard_Real upDirSize = upDir.Magnitude();
2869   if ( upDirSize <= gp::Resolution() ) return false;
2870   upDir / upDirSize;
2871
2872   // Assure that the bottom face normal points up
2873   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2874   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2875   if ( Nb.Dot( upDir ) < 0 ) {
2876     DUMPSO( "Reverse bottom face");
2877     swap( 1, 3, idNodes, P );
2878   }
2879
2880   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2881   Standard_Real minDist = DBL_MAX;
2882   for ( i = 4; i < 8; i++ ) {
2883     // projection of P[i] to the plane defined by P[0] and upDir
2884     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2885     Standard_Real sqDist = P[0].SquareDistance( Pp );
2886     if ( sqDist < minDist ) {
2887       minDist = sqDist;
2888       iMin = i;
2889     }
2890   }
2891   DUMPSO( "Set 4-th");
2892   swap ( 4, iMin, idNodes, P );
2893
2894   // Set nodes of the top face in good order
2895   DUMPSO( "Sort top face");
2896   i = SortQuadNodes( theMesh, &idNodes[4] );
2897   if ( i ) {
2898     i += 4;
2899     gp_Pnt Ptmp = P[ i ];
2900     P[ i ] = P[ i+1 ];
2901     P[ i+1 ] = Ptmp;
2902   }
2903
2904   // Assure that direction of the top face normal is from the bottom face
2905   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2906   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2907   if ( Nt.Dot( upDir ) < 0 ) {
2908     DUMPSO( "Reverse top face");
2909     swap( 5, 7, idNodes, P );
2910   }
2911
2912   //   DUMPSO( "OUTPUT: ========================================");
2913   //   for ( i = 0; i < 8; i++ ) {
2914   //     float *p = ugrid->GetPoint(idNodes[i]);
2915   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2916   //   }
2917
2918   return true;
2919 }*/
2920
2921 //================================================================================
2922 /*!
2923  * \brief Return nodes linked to the given one
2924  * \param theNode - the node
2925  * \param linkedNodes - the found nodes
2926  * \param type - the type of elements to check
2927  *
2928  * Medium nodes are ignored
2929  */
2930 //================================================================================
2931
2932 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2933                                        TIDSortedElemSet &   linkedNodes,
2934                                        SMDSAbs_ElementType  type )
2935 {
2936   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2937   while ( elemIt->more() )
2938   {
2939     const SMDS_MeshElement* elem = elemIt->next();
2940     if(elem->GetType() == SMDSAbs_0DElement)
2941       continue;
2942
2943     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2944     if ( elem->GetType() == SMDSAbs_Volume )
2945     {
2946       SMDS_VolumeTool vol( elem );
2947       while ( nodeIt->more() ) {
2948         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2949         if ( theNode != n && vol.IsLinked( theNode, n ))
2950           linkedNodes.insert( n );
2951       }
2952     }
2953     else
2954     {
2955       for ( int i = 0; nodeIt->more(); ++i ) {
2956         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2957         if ( n == theNode ) {
2958           int iBefore = i - 1;
2959           int iAfter  = i + 1;
2960           if ( elem->IsQuadratic() ) {
2961             int nb = elem->NbNodes() / 2;
2962             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2963             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2964           }
2965           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2966           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2967         }
2968       }
2969     }
2970   }
2971 }
2972
2973 //=======================================================================
2974 //function : laplacianSmooth
2975 //purpose  : pulls theNode toward the center of surrounding nodes directly
2976 //           connected to that node along an element edge
2977 //=======================================================================
2978
2979 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2980                      const Handle(Geom_Surface)&          theSurface,
2981                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2982 {
2983   // find surrounding nodes
2984
2985   TIDSortedElemSet nodeSet;
2986   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2987
2988   // compute new coodrs
2989
2990   double coord[] = { 0., 0., 0. };
2991   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2992   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2993     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2994     if ( theSurface.IsNull() ) { // smooth in 3D
2995       coord[0] += node->X();
2996       coord[1] += node->Y();
2997       coord[2] += node->Z();
2998     }
2999     else { // smooth in 2D
3000       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3001       gp_XY* uv = theUVMap[ node ];
3002       coord[0] += uv->X();
3003       coord[1] += uv->Y();
3004     }
3005   }
3006   int nbNodes = nodeSet.size();
3007   if ( !nbNodes )
3008     return;
3009   coord[0] /= nbNodes;
3010   coord[1] /= nbNodes;
3011
3012   if ( !theSurface.IsNull() ) {
3013     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3014     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3015     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3016     coord[0] = p3d.X();
3017     coord[1] = p3d.Y();
3018     coord[2] = p3d.Z();
3019   }
3020   else
3021     coord[2] /= nbNodes;
3022
3023   // move node
3024
3025   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3026 }
3027
3028 //=======================================================================
3029 //function : centroidalSmooth
3030 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3031 //           surrounding elements
3032 //=======================================================================
3033
3034 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3035                       const Handle(Geom_Surface)&          theSurface,
3036                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3037 {
3038   gp_XYZ aNewXYZ(0.,0.,0.);
3039   SMESH::Controls::Area anAreaFunc;
3040   double totalArea = 0.;
3041   int nbElems = 0;
3042
3043   // compute new XYZ
3044
3045   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3046   while ( elemIt->more() )
3047   {
3048     const SMDS_MeshElement* elem = elemIt->next();
3049     nbElems++;
3050
3051     gp_XYZ elemCenter(0.,0.,0.);
3052     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3053     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3054     int nn = elem->NbNodes();
3055     if(elem->IsQuadratic()) nn = nn/2;
3056     int i=0;
3057     //while ( itN->more() ) {
3058     while ( i<nn ) {
3059       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3060       i++;
3061       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3062       aNodePoints.push_back( aP );
3063       if ( !theSurface.IsNull() ) { // smooth in 2D
3064         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3065         gp_XY* uv = theUVMap[ aNode ];
3066         aP.SetCoord( uv->X(), uv->Y(), 0. );
3067       }
3068       elemCenter += aP;
3069     }
3070     double elemArea = anAreaFunc.GetValue( aNodePoints );
3071     totalArea += elemArea;
3072     elemCenter /= nn;
3073     aNewXYZ += elemCenter * elemArea;
3074   }
3075   aNewXYZ /= totalArea;
3076   if ( !theSurface.IsNull() ) {
3077     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3078     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3079   }
3080
3081   // move node
3082
3083   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3084 }
3085
3086 //=======================================================================
3087 //function : getClosestUV
3088 //purpose  : return UV of closest projection
3089 //=======================================================================
3090
3091 static bool getClosestUV (Extrema_GenExtPS& projector,
3092                           const gp_Pnt&     point,
3093                           gp_XY &           result)
3094 {
3095   projector.Perform( point );
3096   if ( projector.IsDone() ) {
3097     double u, v, minVal = DBL_MAX;
3098     for ( int i = projector.NbExt(); i > 0; i-- )
3099 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3100       if ( projector.SquareDistance( i ) < minVal ) {
3101         minVal = projector.SquareDistance( i );
3102 #else
3103       if ( projector.Value( i ) < minVal ) {
3104         minVal = projector.Value( i );
3105 #endif
3106         projector.Point( i ).Parameter( u, v );
3107       }
3108     result.SetCoord( u, v );
3109     return true;
3110   }
3111   return false;
3112 }
3113
3114 //=======================================================================
3115 //function : Smooth
3116 //purpose  : Smooth theElements during theNbIterations or until a worst
3117 //           element has aspect ratio <= theTgtAspectRatio.
3118 //           Aspect Ratio varies in range [1.0, inf].
3119 //           If theElements is empty, the whole mesh is smoothed.
3120 //           theFixedNodes contains additionally fixed nodes. Nodes built
3121 //           on edges and boundary nodes are always fixed.
3122 //=======================================================================
3123
3124 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3125                                set<const SMDS_MeshNode*> & theFixedNodes,
3126                                const SmoothMethod          theSmoothMethod,
3127                                const int                   theNbIterations,
3128                                double                      theTgtAspectRatio,
3129                                const bool                  the2D)
3130 {
3131   myLastCreatedElems.Clear();
3132   myLastCreatedNodes.Clear();
3133
3134   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3135
3136   if ( theTgtAspectRatio < 1.0 )
3137     theTgtAspectRatio = 1.0;
3138
3139   const double disttol = 1.e-16;
3140
3141   SMESH::Controls::AspectRatio aQualityFunc;
3142
3143   SMESHDS_Mesh* aMesh = GetMeshDS();
3144
3145   if ( theElems.empty() ) {
3146     // add all faces to theElems
3147     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3148     while ( fIt->more() ) {
3149       const SMDS_MeshElement* face = fIt->next();
3150       theElems.insert( theElems.end(), face );
3151     }
3152   }
3153   // get all face ids theElems are on
3154   set< int > faceIdSet;
3155   TIDSortedElemSet::iterator itElem;
3156   if ( the2D )
3157     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3158       int fId = FindShape( *itElem );
3159       // check that corresponding submesh exists and a shape is face
3160       if (fId &&
3161           faceIdSet.find( fId ) == faceIdSet.end() &&
3162           aMesh->MeshElements( fId )) {
3163         TopoDS_Shape F = aMesh->IndexToShape( fId );
3164         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3165           faceIdSet.insert( fId );
3166       }
3167     }
3168   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3169
3170   // ===============================================
3171   // smooth elements on each TopoDS_Face separately
3172   // ===============================================
3173
3174   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3175   for ( ; fId != faceIdSet.rend(); ++fId ) {
3176     // get face surface and submesh
3177     Handle(Geom_Surface) surface;
3178     SMESHDS_SubMesh* faceSubMesh = 0;
3179     TopoDS_Face face;
3180     double fToler2 = 0, f,l;
3181     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3182     bool isUPeriodic = false, isVPeriodic = false;
3183     if ( *fId ) {
3184       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3185       surface = BRep_Tool::Surface( face );
3186       faceSubMesh = aMesh->MeshElements( *fId );
3187       fToler2 = BRep_Tool::Tolerance( face );
3188       fToler2 *= fToler2 * 10.;
3189       isUPeriodic = surface->IsUPeriodic();
3190       if ( isUPeriodic )
3191         surface->UPeriod();
3192       isVPeriodic = surface->IsVPeriodic();
3193       if ( isVPeriodic )
3194         surface->VPeriod();
3195       surface->Bounds( u1, u2, v1, v2 );
3196     }
3197     // ---------------------------------------------------------
3198     // for elements on a face, find movable and fixed nodes and
3199     // compute UV for them
3200     // ---------------------------------------------------------
3201     bool checkBoundaryNodes = false;
3202     bool isQuadratic = false;
3203     set<const SMDS_MeshNode*> setMovableNodes;
3204     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3205     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3206     list< const SMDS_MeshElement* > elemsOnFace;
3207
3208     Extrema_GenExtPS projector;
3209     GeomAdaptor_Surface surfAdaptor;
3210     if ( !surface.IsNull() ) {
3211       surfAdaptor.Load( surface );
3212       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3213     }
3214     int nbElemOnFace = 0;
3215     itElem = theElems.begin();
3216     // loop on not yet smoothed elements: look for elems on a face
3217     while ( itElem != theElems.end() ) {
3218       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3219         break; // all elements found
3220
3221       const SMDS_MeshElement* elem = *itElem;
3222       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3223            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3224         ++itElem;
3225         continue;
3226       }
3227       elemsOnFace.push_back( elem );
3228       theElems.erase( itElem++ );
3229       nbElemOnFace++;
3230
3231       if ( !isQuadratic )
3232         isQuadratic = elem->IsQuadratic();
3233
3234       // get movable nodes of elem
3235       const SMDS_MeshNode* node;
3236       SMDS_TypeOfPosition posType;
3237       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3238       int nn = 0, nbn =  elem->NbNodes();
3239       if(elem->IsQuadratic())
3240         nbn = nbn/2;
3241       while ( nn++ < nbn ) {
3242         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3243         const SMDS_PositionPtr& pos = node->GetPosition();
3244         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3245         if (posType != SMDS_TOP_EDGE &&
3246             posType != SMDS_TOP_VERTEX &&
3247             theFixedNodes.find( node ) == theFixedNodes.end())
3248         {
3249           // check if all faces around the node are on faceSubMesh
3250           // because a node on edge may be bound to face
3251           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3252           bool all = true;
3253           if ( faceSubMesh ) {
3254             while ( eIt->more() && all ) {
3255               const SMDS_MeshElement* e = eIt->next();
3256               all = faceSubMesh->Contains( e );
3257             }
3258           }
3259           if ( all )
3260             setMovableNodes.insert( node );
3261           else
3262             checkBoundaryNodes = true;
3263         }
3264         if ( posType == SMDS_TOP_3DSPACE )
3265           checkBoundaryNodes = true;
3266       }
3267
3268       if ( surface.IsNull() )
3269         continue;
3270
3271       // get nodes to check UV
3272       list< const SMDS_MeshNode* > uvCheckNodes;
3273       itN = elem->nodesIterator();
3274       nn = 0; nbn =  elem->NbNodes();
3275       if(elem->IsQuadratic())
3276         nbn = nbn/2;
3277       while ( nn++ < nbn ) {
3278         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3279         if ( uvMap.find( node ) == uvMap.end() )
3280           uvCheckNodes.push_back( node );
3281         // add nodes of elems sharing node
3282         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3283         //         while ( eIt->more() ) {
3284         //           const SMDS_MeshElement* e = eIt->next();
3285         //           if ( e != elem ) {
3286         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3287         //             while ( nIt->more() ) {
3288         //               const SMDS_MeshNode* n =
3289         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3290         //               if ( uvMap.find( n ) == uvMap.end() )
3291         //                 uvCheckNodes.push_back( n );
3292         //             }
3293         //           }
3294         //         }
3295       }
3296       // check UV on face
3297       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3298       for ( ; n != uvCheckNodes.end(); ++n ) {
3299         node = *n;
3300         gp_XY uv( 0, 0 );
3301         const SMDS_PositionPtr& pos = node->GetPosition();
3302         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3303         // get existing UV
3304         switch ( posType ) {
3305         case SMDS_TOP_FACE: {
3306           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3307           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3308           break;
3309         }
3310         case SMDS_TOP_EDGE: {
3311           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3312           Handle(Geom2d_Curve) pcurve;
3313           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3314             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3315           if ( !pcurve.IsNull() ) {
3316             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3317             uv = pcurve->Value( u ).XY();
3318           }
3319           break;
3320         }
3321         case SMDS_TOP_VERTEX: {
3322           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3323           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3324             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3325           break;
3326         }
3327         default:;
3328         }
3329         // check existing UV
3330         bool project = true;
3331         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3332         double dist1 = DBL_MAX, dist2 = 0;
3333         if ( posType != SMDS_TOP_3DSPACE ) {
3334           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3335           project = dist1 > fToler2;
3336         }
3337         if ( project ) { // compute new UV
3338           gp_XY newUV;
3339           if ( !getClosestUV( projector, pNode, newUV )) {
3340             MESSAGE("Node Projection Failed " << node);
3341           }
3342           else {
3343             if ( isUPeriodic )
3344               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3345             if ( isVPeriodic )
3346               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3347             // check new UV
3348             if ( posType != SMDS_TOP_3DSPACE )
3349               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3350             if ( dist2 < dist1 )
3351               uv = newUV;
3352           }
3353         }
3354         // store UV in the map
3355         listUV.push_back( uv );
3356         uvMap.insert( make_pair( node, &listUV.back() ));
3357       }
3358     } // loop on not yet smoothed elements
3359
3360     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3361       checkBoundaryNodes = true;
3362
3363     // fix nodes on mesh boundary
3364
3365     if ( checkBoundaryNodes ) {
3366       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3367       map< SMESH_TLink, int >::iterator link_nb;
3368       // put all elements links to linkNbMap
3369       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3370       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3371         const SMDS_MeshElement* elem = (*elemIt);
3372         int nbn =  elem->NbCornerNodes();
3373         // loop on elem links: insert them in linkNbMap
3374         for ( int iN = 0; iN < nbn; ++iN ) {
3375           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3376           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3377           SMESH_TLink link( n1, n2 );
3378           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3379           link_nb->second++;
3380         }
3381       }
3382       // remove nodes that are in links encountered only once from setMovableNodes
3383       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3384         if ( link_nb->second == 1 ) {
3385           setMovableNodes.erase( link_nb->first.node1() );
3386           setMovableNodes.erase( link_nb->first.node2() );
3387         }
3388       }
3389     }
3390
3391     // -----------------------------------------------------
3392     // for nodes on seam edge, compute one more UV ( uvMap2 );
3393     // find movable nodes linked to nodes on seam and which
3394     // are to be smoothed using the second UV ( uvMap2 )
3395     // -----------------------------------------------------
3396
3397     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3398     if ( !surface.IsNull() ) {
3399       TopExp_Explorer eExp( face, TopAbs_EDGE );
3400       for ( ; eExp.More(); eExp.Next() ) {
3401         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3402         if ( !BRep_Tool::IsClosed( edge, face ))
3403           continue;
3404         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3405         if ( !sm ) continue;
3406         // find out which parameter varies for a node on seam
3407         double f,l;
3408         gp_Pnt2d uv1, uv2;
3409         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3410         if ( pcurve.IsNull() ) continue;
3411         uv1 = pcurve->Value( f );
3412         edge.Reverse();
3413         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3414         if ( pcurve.IsNull() ) continue;
3415         uv2 = pcurve->Value( f );
3416         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3417         // assure uv1 < uv2
3418         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3419           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3420         }
3421         // get nodes on seam and its vertices
3422         list< const SMDS_MeshNode* > seamNodes;
3423         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3424         while ( nSeamIt->more() ) {
3425           const SMDS_MeshNode* node = nSeamIt->next();
3426           if ( !isQuadratic || !IsMedium( node ))
3427             seamNodes.push_back( node );
3428         }
3429         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3430         for ( ; vExp.More(); vExp.Next() ) {
3431           sm = aMesh->MeshElements( vExp.Current() );
3432           if ( sm ) {
3433             nSeamIt = sm->GetNodes();
3434             while ( nSeamIt->more() )
3435               seamNodes.push_back( nSeamIt->next() );
3436           }
3437         }
3438         // loop on nodes on seam
3439         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3440         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3441           const SMDS_MeshNode* nSeam = *noSeIt;
3442           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3443           if ( n_uv == uvMap.end() )
3444             continue;
3445           // set the first UV
3446           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3447           // set the second UV
3448           listUV.push_back( *n_uv->second );
3449           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3450           if ( uvMap2.empty() )
3451             uvMap2 = uvMap; // copy the uvMap contents
3452           uvMap2[ nSeam ] = &listUV.back();
3453
3454           // collect movable nodes linked to ones on seam in nodesNearSeam
3455           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3456           while ( eIt->more() ) {
3457             const SMDS_MeshElement* e = eIt->next();
3458             int nbUseMap1 = 0, nbUseMap2 = 0;
3459             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3460             int nn = 0, nbn =  e->NbNodes();
3461             if(e->IsQuadratic()) nbn = nbn/2;
3462             while ( nn++ < nbn )
3463             {
3464               const SMDS_MeshNode* n =
3465                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3466               if (n == nSeam ||
3467                   setMovableNodes.find( n ) == setMovableNodes.end() )
3468                 continue;
3469               // add only nodes being closer to uv2 than to uv1
3470               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3471                            0.5 * ( n->Y() + nSeam->Y() ),
3472                            0.5 * ( n->Z() + nSeam->Z() ));
3473               gp_XY uv;
3474               getClosestUV( projector, pMid, uv );
3475               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3476                 nodesNearSeam.insert( n );
3477                 nbUseMap2++;
3478               }
3479               else
3480                 nbUseMap1++;
3481             }
3482             // for centroidalSmooth all element nodes must
3483             // be on one side of a seam
3484             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3485               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3486               nn = 0;
3487               while ( nn++ < nbn ) {
3488                 const SMDS_MeshNode* n =
3489                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3490                 setMovableNodes.erase( n );
3491               }
3492             }
3493           }
3494         } // loop on nodes on seam
3495       } // loop on edge of a face
3496     } // if ( !face.IsNull() )
3497
3498     if ( setMovableNodes.empty() ) {
3499       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3500       continue; // goto next face
3501     }
3502
3503     // -------------
3504     // SMOOTHING //
3505     // -------------
3506
3507     int it = -1;
3508     double maxRatio = -1., maxDisplacement = -1.;
3509     set<const SMDS_MeshNode*>::iterator nodeToMove;
3510     for ( it = 0; it < theNbIterations; it++ ) {
3511       maxDisplacement = 0.;
3512       nodeToMove = setMovableNodes.begin();
3513       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3514         const SMDS_MeshNode* node = (*nodeToMove);
3515         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3516
3517         // smooth
3518         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3519         if ( theSmoothMethod == LAPLACIAN )
3520           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3521         else
3522           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3523
3524         // node displacement
3525         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3526         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3527         if ( aDispl > maxDisplacement )
3528           maxDisplacement = aDispl;
3529       }
3530       // no node movement => exit
3531       //if ( maxDisplacement < 1.e-16 ) {
3532       if ( maxDisplacement < disttol ) {
3533         MESSAGE("-- no node movement --");
3534         break;
3535       }
3536
3537       // check elements quality
3538       maxRatio  = 0;
3539       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3540       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3541         const SMDS_MeshElement* elem = (*elemIt);
3542         if ( !elem || elem->GetType() != SMDSAbs_Face )
3543           continue;
3544         SMESH::Controls::TSequenceOfXYZ aPoints;
3545         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3546           double aValue = aQualityFunc.GetValue( aPoints );
3547           if ( aValue > maxRatio )
3548             maxRatio = aValue;
3549         }
3550       }
3551       if ( maxRatio <= theTgtAspectRatio ) {
3552         MESSAGE("-- quality achived --");
3553         break;
3554       }
3555       if (it+1 == theNbIterations) {
3556         MESSAGE("-- Iteration limit exceeded --");
3557       }
3558     } // smoothing iterations
3559
3560     MESSAGE(" Face id: " << *fId <<
3561             " Nb iterstions: " << it <<
3562             " Displacement: " << maxDisplacement <<
3563             " Aspect Ratio " << maxRatio);
3564
3565     // ---------------------------------------
3566     // new nodes positions are computed,
3567     // record movement in DS and set new UV
3568     // ---------------------------------------
3569     nodeToMove = setMovableNodes.begin();
3570     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3571       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3572       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3573       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3574       if ( node_uv != uvMap.end() ) {
3575         gp_XY* uv = node_uv->second;
3576         node->SetPosition
3577           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3578       }
3579     }
3580
3581     // move medium nodes of quadratic elements
3582     if ( isQuadratic )
3583     {
3584       SMESH_MesherHelper helper( *GetMesh() );
3585       if ( !face.IsNull() )
3586         helper.SetSubShape( face );
3587       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3588       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3589         const SMDS_VtkFace* QF =
3590           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3591         if(QF && QF->IsQuadratic()) {
3592           vector<const SMDS_MeshNode*> Ns;
3593           Ns.reserve(QF->NbNodes()+1);
3594           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3595           while ( anIter->more() )
3596             Ns.push_back( cast2Node(anIter->next()) );
3597           Ns.push_back( Ns[0] );
3598           double x, y, z;
3599           for(int i=0; i<QF->NbNodes(); i=i+2) {
3600             if ( !surface.IsNull() ) {
3601               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3602               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3603               gp_XY uv = ( uv1 + uv2 ) / 2.;
3604               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3605               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3606             }
3607             else {
3608               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3609               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3610               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3611             }
3612             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3613                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3614                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3615               // we have to move i+1 node
3616               aMesh->MoveNode( Ns[i+1], x, y, z );
3617             }
3618           }
3619         }
3620       }
3621     }
3622
3623   } // loop on face ids
3624
3625 }
3626
3627 //=======================================================================
3628 //function : isReverse
3629 //purpose  : Return true if normal of prevNodes is not co-directied with
3630 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3631 //           iNotSame is where prevNodes and nextNodes are different.
3632 //           If result is true then future volume orientation is OK
3633 //=======================================================================
3634
3635 static bool isReverse(const SMDS_MeshElement*             face,
3636                       const vector<const SMDS_MeshNode*>& prevNodes,
3637                       const vector<const SMDS_MeshNode*>& nextNodes,
3638                       const int                           iNotSame)
3639 {
3640
3641   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3642   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3643   gp_XYZ extrDir( pN - pP ), faceNorm;
3644   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3645
3646   return faceNorm * extrDir < 0.0;
3647 }
3648
3649 //=======================================================================
3650 /*!
3651  * \brief Create elements by sweeping an element
3652  * \param elem - element to sweep
3653  * \param newNodesItVec - nodes generated from each node of the element
3654  * \param newElems - generated elements
3655  * \param nbSteps - number of sweeping steps
3656  * \param srcElements - to append elem for each generated element
3657  */
3658 //=======================================================================
3659
3660 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3661                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3662                                     list<const SMDS_MeshElement*>&        newElems,
3663                                     const int                             nbSteps,
3664                                     SMESH_SequenceOfElemPtr&              srcElements)
3665 {
3666   //MESSAGE("sweepElement " << nbSteps);
3667   SMESHDS_Mesh* aMesh = GetMeshDS();
3668
3669   const int           nbNodes = elem->NbNodes();
3670   const int         nbCorners = elem->NbCornerNodes();
3671   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3672                                                           polyhedron creation !!! */
3673   // Loop on elem nodes:
3674   // find new nodes and detect same nodes indices
3675   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3676   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3677   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3678   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3679
3680   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3681   vector<int> sames(nbNodes);
3682   vector<bool> isSingleNode(nbNodes);
3683
3684   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3685     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3686     const SMDS_MeshNode*                         node = nnIt->first;
3687     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3688     if ( listNewNodes.empty() )
3689       return;
3690
3691     itNN   [ iNode ] = listNewNodes.begin();
3692     prevNod[ iNode ] = node;
3693     nextNod[ iNode ] = listNewNodes.front();
3694
3695     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3696                                                              corner node of linear */
3697     if ( prevNod[ iNode ] != nextNod [ iNode ])
3698       nbDouble += !isSingleNode[iNode];
3699
3700     if( iNode < nbCorners ) { // check corners only
3701       if ( prevNod[ iNode ] == nextNod [ iNode ])
3702         sames[nbSame++] = iNode;
3703       else
3704         iNotSameNode = iNode;
3705     }
3706   }
3707
3708   if ( nbSame == nbNodes || nbSame > 2) {
3709     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3710     return;
3711   }
3712
3713   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3714   {
3715     // fix nodes order to have bottom normal external
3716     if ( baseType == SMDSEntity_Polygon )
3717     {
3718       std::reverse( itNN.begin(), itNN.end() );
3719       std::reverse( prevNod.begin(), prevNod.end() );
3720       std::reverse( midlNod.begin(), midlNod.end() );
3721       std::reverse( nextNod.begin(), nextNod.end() );
3722       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3723     }
3724     else
3725     {
3726       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3727       SMDS_MeshCell::applyInterlace( ind, itNN );
3728       SMDS_MeshCell::applyInterlace( ind, prevNod );
3729       SMDS_MeshCell::applyInterlace( ind, nextNod );
3730       SMDS_MeshCell::applyInterlace( ind, midlNod );
3731       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3732       if ( nbSame > 0 )
3733       {
3734         sames[nbSame] = iNotSameNode;
3735         for ( int j = 0; j <= nbSame; ++j )
3736           for ( size_t i = 0; i < ind.size(); ++i )
3737             if ( ind[i] == sames[j] )
3738             {
3739               sames[j] = i;
3740               break;
3741             }
3742         iNotSameNode = sames[nbSame];
3743       }
3744     }
3745   }
3746
3747   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3748   if ( nbSame > 0 ) {
3749     iSameNode    = sames[ nbSame-1 ];
3750     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3751     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3752     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3753   }
3754
3755   // make new elements
3756   for (int iStep = 0; iStep < nbSteps; iStep++ )
3757   {
3758     // get next nodes
3759     for ( iNode = 0; iNode < nbNodes; iNode++ )
3760     {
3761       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3762       nextNod[ iNode ] = *itNN[ iNode ]++;
3763     }
3764
3765     SMDS_MeshElement* aNewElem = 0;
3766     /*if(!elem->IsPoly())*/ {
3767       switch ( baseType ) {
3768       case SMDSEntity_0D:
3769       case SMDSEntity_Node: { // sweep NODE
3770         if ( nbSame == 0 ) {
3771           if ( isSingleNode[0] )
3772             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3773           else
3774             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3775         }
3776         else
3777           return;
3778         break;
3779       }
3780       case SMDSEntity_Edge: { // sweep EDGE
3781         if ( nbDouble == 0 )
3782         {
3783           if ( nbSame == 0 ) // ---> quadrangle
3784             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3785                                       nextNod[ 1 ], nextNod[ 0 ] );
3786           else               // ---> triangle
3787             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3788                                       nextNod[ iNotSameNode ] );
3789         }
3790         else                 // ---> polygon
3791         {
3792           vector<const SMDS_MeshNode*> poly_nodes;
3793           poly_nodes.push_back( prevNod[0] );
3794           poly_nodes.push_back( prevNod[1] );
3795           if ( prevNod[1] != nextNod[1] )
3796           {
3797             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3798             poly_nodes.push_back( nextNod[1] );
3799           }
3800           if ( prevNod[0] != nextNod[0] )
3801           {
3802             poly_nodes.push_back( nextNod[0] );
3803             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3804           }
3805           switch ( poly_nodes.size() ) {
3806           case 3:
3807             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3808             break;
3809           case 4:
3810             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3811                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3812             break;
3813           default:
3814             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3815           }
3816         }
3817         break;
3818       }
3819       case SMDSEntity_Triangle: // TRIANGLE --->
3820         {
3821           if ( nbDouble > 0 ) break;
3822           if ( nbSame == 0 )       // ---> pentahedron
3823             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3824                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3825
3826           else if ( nbSame == 1 )  // ---> pyramid
3827             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3828                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3829                                          nextNod[ iSameNode ]);
3830
3831           else // 2 same nodes:       ---> tetrahedron
3832             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3833                                          nextNod[ iNotSameNode ]);
3834           break;
3835         }
3836       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3837         {
3838           if ( nbSame == 2 )
3839             return;
3840           if ( nbDouble+nbSame == 2 )
3841           {
3842             if(nbSame==0) {      // ---> quadratic quadrangle
3843               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3844                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3845             }
3846             else { //(nbSame==1) // ---> quadratic triangle
3847               if(sames[0]==2) {
3848                 return; // medium node on axis
3849               }
3850               else if(sames[0]==0)
3851                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3852                                           nextNod[2], midlNod[1], prevNod[2]);
3853               else // sames[0]==1
3854                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3855                                           midlNod[0], nextNod[2], prevNod[2]);
3856             }
3857           }
3858           else if ( nbDouble == 3 )
3859           {
3860             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3861               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3862                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3863             }
3864           }
3865           else
3866             return;
3867           break;
3868         }
3869       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3870         if ( nbDouble > 0 ) break;
3871
3872         if ( nbSame == 0 )       // ---> hexahedron
3873           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3874                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3875
3876         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3877           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3878                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3879                                        nextNod[ iSameNode ]);
3880           newElems.push_back( aNewElem );
3881           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3882                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3883                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3884         }
3885         else if ( nbSame == 2 ) { // ---> pentahedron
3886           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3887             // iBeforeSame is same too
3888             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3889                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3890                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3891           else
3892             // iAfterSame is same too
3893             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3894                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3895                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3896         }
3897         break;
3898       }
3899       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3900         if ( nbDouble+nbSame != 3 ) break;
3901         if(nbSame==0) {
3902           // --->  pentahedron with 15 nodes
3903           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3904                                        nextNod[0], nextNod[1], nextNod[2],
3905                                        prevNod[3], prevNod[4], prevNod[5],
3906                                        nextNod[3], nextNod[4], nextNod[5],
3907                                        midlNod[0], midlNod[1], midlNod[2]);
3908         }
3909         else if(nbSame==1) {
3910           // --->  2d order pyramid of 13 nodes
3911           int apex = iSameNode;
3912           int i0 = ( apex + 1 ) % nbCorners;
3913           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3914           int i0a = apex + 3;
3915           int i1a = i1 + 3;
3916           int i01 = i0 + 3;
3917           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3918                                       nextNod[i0], nextNod[i1], prevNod[apex],
3919                                       prevNod[i01], midlNod[i0],
3920                                       nextNod[i01], midlNod[i1],
3921                                       prevNod[i1a], prevNod[i0a],
3922                                       nextNod[i0a], nextNod[i1a]);
3923         }
3924         else if(nbSame==2) {
3925           // --->  2d order tetrahedron of 10 nodes
3926           int n1 = iNotSameNode;
3927           int n2 = ( n1 + 1             ) % nbCorners;
3928           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3929           int n12 = n1 + 3;
3930           int n23 = n2 + 3;
3931           int n31 = n3 + 3;
3932           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3933                                        prevNod[n12], prevNod[n23], prevNod[n31],
3934                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3935         }
3936         break;
3937       }
3938       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3939         if( nbSame == 0 ) {
3940           if ( nbDouble != 4 ) break;
3941           // --->  hexahedron with 20 nodes
3942           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3943                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3944                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3945                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3946                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3947         }
3948         else if(nbSame==1) {
3949           // ---> pyramid + pentahedron - can not be created since it is needed
3950           // additional middle node at the center of face
3951           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3952           return;
3953         }
3954         else if( nbSame == 2 ) {
3955           if ( nbDouble != 2 ) break;
3956           // --->  2d order Pentahedron with 15 nodes
3957           int n1,n2,n4,n5;
3958           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3959             // iBeforeSame is same too
3960             n1 = iBeforeSame;
3961             n2 = iOpposSame;
3962             n4 = iSameNode;
3963             n5 = iAfterSame;
3964           }
3965           else {
3966             // iAfterSame is same too
3967             n1 = iSameNode;
3968             n2 = iBeforeSame;
3969             n4 = iAfterSame;
3970             n5 = iOpposSame;
3971           }
3972           int n12 = n2 + 4;
3973           int n45 = n4 + 4;
3974           int n14 = n1 + 4;
3975           int n25 = n5 + 4;
3976           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3977                                        prevNod[n4], prevNod[n5], nextNod[n5],
3978                                        prevNod[n12], midlNod[n2], nextNod[n12],
3979                                        prevNod[n45], midlNod[n5], nextNod[n45],
3980                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3981         }
3982         break;
3983       }
3984       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3985
3986         if( nbSame == 0 && nbDouble == 9 ) {
3987           // --->  tri-quadratic hexahedron with 27 nodes
3988           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3989                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3990                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3991                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3992                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3993                                        prevNod[8], // bottom center
3994                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3995                                        nextNod[8], // top center
3996                                        midlNod[8]);// elem center
3997         }
3998         else
3999         {
4000           return;
4001         }
4002         break;
4003       }
4004       case SMDSEntity_Polygon: { // sweep POLYGON
4005
4006         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4007           // --->  hexagonal prism
4008           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4009                                        prevNod[3], prevNod[4], prevNod[5],
4010                                        nextNod[0], nextNod[1], nextNod[2],
4011                                        nextNod[3], nextNod[4], nextNod[5]);
4012         }
4013         break;
4014       }
4015       case SMDSEntity_Ball:
4016         return;
4017
4018       default:
4019         break;
4020       }
4021     }
4022
4023     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4024     {
4025       if ( baseType != SMDSEntity_Polygon )
4026       {
4027         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4028         SMDS_MeshCell::applyInterlace( ind, prevNod );
4029         SMDS_MeshCell::applyInterlace( ind, nextNod );
4030         SMDS_MeshCell::applyInterlace( ind, midlNod );
4031         SMDS_MeshCell::applyInterlace( ind, itNN );
4032         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4033         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4034       }
4035       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4036       vector<int> quantities (nbNodes + 2);
4037       polyedre_nodes.clear();
4038       quantities.clear();
4039
4040       // bottom of prism
4041       for (int inode = 0; inode < nbNodes; inode++)
4042         polyedre_nodes.push_back( prevNod[inode] );
4043       quantities.push_back( nbNodes );
4044
4045       // top of prism
4046       polyedre_nodes.push_back( nextNod[0] );
4047       for (int inode = nbNodes; inode-1; --inode )
4048         polyedre_nodes.push_back( nextNod[inode-1] );
4049       quantities.push_back( nbNodes );
4050
4051       // side faces
4052       for (int iface = 0; iface < nbNodes; iface++)
4053       {
4054         const int prevNbNodes = polyedre_nodes.size();
4055         int inextface = (iface+1) % nbNodes;
4056         polyedre_nodes.push_back( prevNod[inextface] );
4057         polyedre_nodes.push_back( prevNod[iface] );
4058         if ( prevNod[iface] != nextNod[iface] )
4059         {
4060           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4061           polyedre_nodes.push_back( nextNod[iface] );
4062         }
4063         if ( prevNod[inextface] != nextNod[inextface] )
4064         {
4065           polyedre_nodes.push_back( nextNod[inextface] );
4066           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4067         }
4068         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4069         if ( nbFaceNodes > 2 )
4070           quantities.push_back( nbFaceNodes );
4071         else // degenerated face
4072           polyedre_nodes.resize( prevNbNodes );
4073       }
4074       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4075     }
4076
4077     if ( aNewElem ) {
4078       newElems.push_back( aNewElem );
4079       myLastCreatedElems.Append(aNewElem);
4080       srcElements.Append( elem );
4081     }
4082
4083     // set new prev nodes
4084     for ( iNode = 0; iNode < nbNodes; iNode++ )
4085       prevNod[ iNode ] = nextNod[ iNode ];
4086
4087   } // for steps
4088 }
4089
4090 //=======================================================================
4091 /*!
4092  * \brief Create 1D and 2D elements around swept elements
4093  * \param mapNewNodes - source nodes and ones generated from them
4094  * \param newElemsMap - source elements and ones generated from them
4095  * \param elemNewNodesMap - nodes generated from each node of each element
4096  * \param elemSet - all swept elements
4097  * \param nbSteps - number of sweeping steps
4098  * \param srcElements - to append elem for each generated element
4099  */
4100 //=======================================================================
4101
4102 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4103                                   TElemOfElemListMap &     newElemsMap,
4104                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4105                                   TIDSortedElemSet&        elemSet,
4106                                   const int                nbSteps,
4107                                   SMESH_SequenceOfElemPtr& srcElements)
4108 {
4109   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4110   SMESHDS_Mesh* aMesh = GetMeshDS();
4111
4112   // Find nodes belonging to only one initial element - sweep them to get edges.
4113
4114   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4115   for ( ; nList != mapNewNodes.end(); nList++ )
4116   {
4117     const SMDS_MeshNode* node =
4118       static_cast<const SMDS_MeshNode*>( nList->first );
4119     if ( newElemsMap.count( node ))
4120       continue; // node was extruded into edge
4121     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4122     int nbInitElems = 0;
4123     const SMDS_MeshElement* el = 0;
4124     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4125     while ( eIt->more() && nbInitElems < 2 ) {
4126       el = eIt->next();
4127       SMDSAbs_ElementType type = el->GetType();
4128       if ( type == SMDSAbs_Volume || type < highType ) continue;
4129       if ( type > highType ) {
4130         nbInitElems = 0;
4131         highType = type;
4132       }
4133       nbInitElems += elemSet.count(el);
4134     }
4135     if ( nbInitElems < 2 ) {
4136       bool NotCreateEdge = el && el->IsMediumNode(node);
4137       if(!NotCreateEdge) {
4138         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4139         list<const SMDS_MeshElement*> newEdges;
4140         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4141       }
4142     }
4143   }
4144
4145   // Make a ceiling for each element ie an equal element of last new nodes.
4146   // Find free links of faces - make edges and sweep them into faces.
4147
4148   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4149   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4150   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4151   {
4152     const SMDS_MeshElement* elem = itElem->first;
4153     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4154
4155     if(itElem->second.size()==0) continue;
4156
4157     const bool isQuadratic = elem->IsQuadratic();
4158
4159     if ( elem->GetType() == SMDSAbs_Edge ) {
4160       // create a ceiling edge
4161       if ( !isQuadratic ) {
4162         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4163                                vecNewNodes[ 1 ]->second.back())) {
4164           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4165                                                    vecNewNodes[ 1 ]->second.back()));
4166           srcElements.Append( myLastCreatedElems.Last() );
4167         }
4168       }
4169       else {
4170         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4171                                vecNewNodes[ 1 ]->second.back(),
4172                                vecNewNodes[ 2 ]->second.back())) {
4173           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4174                                                    vecNewNodes[ 1 ]->second.back(),
4175                                                    vecNewNodes[ 2 ]->second.back()));
4176           srcElements.Append( myLastCreatedElems.Last() );
4177         }
4178       }
4179     }
4180     if ( elem->GetType() != SMDSAbs_Face )
4181       continue;
4182
4183     bool hasFreeLinks = false;
4184
4185     TIDSortedElemSet avoidSet;
4186     avoidSet.insert( elem );
4187
4188     set<const SMDS_MeshNode*> aFaceLastNodes;
4189     int iNode, nbNodes = vecNewNodes.size();
4190     if ( !isQuadratic ) {
4191       // loop on the face nodes
4192       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4193         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4194         // look for free links of the face
4195         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4196         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4197         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4198         // check if a link is free
4199         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4200           hasFreeLinks = true;
4201           // make an edge and a ceiling for a new edge
4202           if ( !aMesh->FindEdge( n1, n2 )) {
4203             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4204             srcElements.Append( myLastCreatedElems.Last() );
4205           }
4206           n1 = vecNewNodes[ iNode ]->second.back();
4207           n2 = vecNewNodes[ iNext ]->second.back();
4208           if ( !aMesh->FindEdge( n1, n2 )) {
4209             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4210             srcElements.Append( myLastCreatedElems.Last() );
4211           }
4212         }
4213       }
4214     }
4215     else { // elem is quadratic face
4216       int nbn = nbNodes/2;
4217       for ( iNode = 0; iNode < nbn; iNode++ ) {
4218         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4219         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4220         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4221         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4222         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4223         // check if a link is free
4224         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4225              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4226              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4227           hasFreeLinks = true;
4228           // make an edge and a ceiling for a new edge
4229           // find medium node
4230           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4231             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4232             srcElements.Append( myLastCreatedElems.Last() );
4233           }
4234           n1 = vecNewNodes[ iNode ]->second.back();
4235           n2 = vecNewNodes[ iNext ]->second.back();
4236           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4237           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4238             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4239             srcElements.Append( myLastCreatedElems.Last() );
4240           }
4241         }
4242       }
4243       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4244         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4245       }
4246     }
4247
4248     // sweep free links into faces
4249
4250     if ( hasFreeLinks )  {
4251       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4252       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4253
4254       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4255       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4256         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4257         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4258       }
4259       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4260         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4261         std::advance( v, volNb );
4262         // find indices of free faces of a volume and their source edges
4263         list< int > freeInd;
4264         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4265         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4266         int iF, nbF = vTool.NbFaces();
4267         for ( iF = 0; iF < nbF; iF ++ ) {
4268           if (vTool.IsFreeFace( iF ) &&
4269               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4270               initNodeSet != faceNodeSet) // except an initial face
4271           {
4272             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4273               continue;
4274             freeInd.push_back( iF );
4275             // find source edge of a free face iF
4276             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4277             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4278             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4279                                    initNodeSet.begin(), initNodeSet.end(),
4280                                    commonNodes.begin());
4281             if ( (*v)->IsQuadratic() )
4282               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4283             else
4284               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4285 #ifdef _DEBUG_
4286             if ( !srcEdges.back() )
4287             {
4288               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4289                    << iF << " of volume #" << vTool.ID() << endl;
4290             }
4291 #endif
4292           }
4293         }
4294         if ( freeInd.empty() )
4295           continue;
4296
4297         // create faces for all steps;
4298         // if such a face has been already created by sweep of edge,
4299         // assure that its orientation is OK
4300         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4301           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4302           vTool.SetExternalNormal();
4303           const int nextShift = vTool.IsForward() ? +1 : -1;
4304           list< int >::iterator ind = freeInd.begin();
4305           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4306           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4307           {
4308             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4309             int nbn = vTool.NbFaceNodes( *ind );
4310             const SMDS_MeshElement * f = 0;
4311             if ( nbn == 3 )              ///// triangle
4312             {
4313               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4314               if ( !f ||
4315                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4316               {
4317                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4318                                                      nodes[ 1 ],
4319                                                      nodes[ 1 + nextShift ] };
4320                 if ( f )
4321                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4322                 else
4323                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4324                                                             newOrder[ 2 ] ));
4325               }
4326             }
4327             else if ( nbn == 4 )       ///// quadrangle
4328             {
4329               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4330               if ( !f ||
4331                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4332               {
4333                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4334                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4335                 if ( f )
4336                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4337                 else
4338                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4339                                                             newOrder[ 2 ], newOrder[ 3 ]));
4340               }
4341             }
4342             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4343             {
4344               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4345               if ( !f ||
4346                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4347               {
4348                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4349                                                      nodes[2],
4350                                                      nodes[2 + 2*nextShift],
4351                                                      nodes[3 - 2*nextShift],
4352                                                      nodes[3],
4353                                                      nodes[3 + 2*nextShift]};
4354                 if ( f )
4355                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4356                 else
4357                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4358                                                             newOrder[ 1 ],
4359                                                             newOrder[ 2 ],
4360                                                             newOrder[ 3 ],
4361                                                             newOrder[ 4 ],
4362                                                             newOrder[ 5 ] ));
4363               }
4364             }
4365             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4366             {
4367               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4368                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4369               if ( !f ||
4370                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4371               {
4372                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4373                                                      nodes[4 - 2*nextShift],
4374                                                      nodes[4],
4375                                                      nodes[4 + 2*nextShift],
4376                                                      nodes[1],
4377                                                      nodes[5 - 2*nextShift],
4378                                                      nodes[5],
4379                                                      nodes[5 + 2*nextShift] };
4380                 if ( f )
4381                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4382                 else
4383                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4384                                                            newOrder[ 2 ], newOrder[ 3 ],
4385                                                            newOrder[ 4 ], newOrder[ 5 ],
4386                                                            newOrder[ 6 ], newOrder[ 7 ]));
4387               }
4388             }
4389             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4390             {
4391               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4392                                       SMDSAbs_Face, /*noMedium=*/false);
4393               if ( !f ||
4394                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4395               {
4396                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4397                                                      nodes[4 - 2*nextShift],
4398                                                      nodes[4],
4399                                                      nodes[4 + 2*nextShift],
4400                                                      nodes[1],
4401                                                      nodes[5 - 2*nextShift],
4402                                                      nodes[5],
4403                                                      nodes[5 + 2*nextShift],
4404                                                      nodes[8] };
4405                 if ( f )
4406                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4407                 else
4408                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4409                                                            newOrder[ 2 ], newOrder[ 3 ],
4410                                                            newOrder[ 4 ], newOrder[ 5 ],
4411                                                            newOrder[ 6 ], newOrder[ 7 ],
4412                                                            newOrder[ 8 ]));
4413               }
4414             }
4415             else  //////// polygon
4416             {
4417               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4418               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4419               if ( !f ||
4420                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4421               {
4422                 if ( !vTool.IsForward() )
4423                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4424                 if ( f )
4425                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4426                 else
4427                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4428               }
4429             }
4430
4431             while ( srcElements.Length() < myLastCreatedElems.Length() )
4432               srcElements.Append( *srcEdge );
4433
4434           }  // loop on free faces
4435
4436           // go to the next volume
4437           iVol = 0;
4438           while ( iVol++ < nbVolumesByStep ) v++;
4439
4440         } // loop on steps
4441       } // loop on volumes of one step
4442     } // sweep free links into faces
4443
4444     // Make a ceiling face with a normal external to a volume
4445
4446     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4447
4448     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4449     if ( iF >= 0 ) {
4450       lastVol.SetExternalNormal();
4451       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4452       int nbn = lastVol.NbFaceNodes( iF );
4453       if ( nbn == 3 ) {
4454         if (!hasFreeLinks ||
4455             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4456           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4457       }
4458       else if ( nbn == 4 )
4459       {
4460         if (!hasFreeLinks ||
4461             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4462           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4463       }
4464       else if ( nbn == 6 && isQuadratic )
4465       {
4466         if (!hasFreeLinks ||
4467             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4468           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4469                                                    nodes[1], nodes[3], nodes[5]));
4470       }
4471       else if ( nbn == 8 && isQuadratic )
4472       {
4473         if (!hasFreeLinks ||
4474             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4475                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4476           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4477                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4478       }
4479       else if ( nbn == 9 && isQuadratic )
4480       {
4481         if (!hasFreeLinks ||
4482             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4483                                 SMDSAbs_Face, /*noMedium=*/false) )
4484           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4485                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4486                                                    nodes[8]));
4487       }
4488       else {
4489         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4490         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4491           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4492       }
4493
4494       while ( srcElements.Length() < myLastCreatedElems.Length() )
4495         srcElements.Append( myLastCreatedElems.Last() );
4496     }
4497   } // loop on swept elements
4498 }
4499
4500 //=======================================================================
4501 //function : RotationSweep
4502 //purpose  :
4503 //=======================================================================
4504
4505 SMESH_MeshEditor::PGroupIDs
4506 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4507                                 const gp_Ax1&      theAxis,
4508                                 const double       theAngle,
4509                                 const int          theNbSteps,
4510                                 const double       theTol,
4511                                 const bool         theMakeGroups,
4512                                 const bool         theMakeWalls)
4513 {
4514   myLastCreatedElems.Clear();
4515   myLastCreatedNodes.Clear();
4516
4517   // source elements for each generated one
4518   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4519
4520   MESSAGE( "RotationSweep()");
4521   gp_Trsf aTrsf;
4522   aTrsf.SetRotation( theAxis, theAngle );
4523   gp_Trsf aTrsf2;
4524   aTrsf2.SetRotation( theAxis, theAngle/2. );
4525
4526   gp_Lin aLine( theAxis );
4527   double aSqTol = theTol * theTol;
4528
4529   SMESHDS_Mesh* aMesh = GetMeshDS();
4530
4531   TNodeOfNodeListMap mapNewNodes;
4532   TElemOfVecOfNnlmiMap mapElemNewNodes;
4533   TElemOfElemListMap newElemsMap;
4534
4535   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4536                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4537                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4538   // loop on theElems
4539   TIDSortedElemSet::iterator itElem;
4540   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4541     const SMDS_MeshElement* elem = *itElem;
4542     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4543       continue;
4544     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4545     newNodesItVec.reserve( elem->NbNodes() );
4546
4547     // loop on elem nodes
4548     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4549     while ( itN->more() )
4550     {
4551       // check if a node has been already sweeped
4552       const SMDS_MeshNode* node = cast2Node( itN->next() );
4553
4554       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4555       double coord[3];
4556       aXYZ.Coord( coord[0], coord[1], coord[2] );
4557       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4558
4559       TNodeOfNodeListMapItr nIt =
4560         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4561       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4562       if ( listNewNodes.empty() )
4563       {
4564         // check if we are to create medium nodes between corner ones
4565         bool needMediumNodes = false;
4566         if ( isQuadraticMesh )
4567         {
4568           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4569           while (it->more() && !needMediumNodes )
4570           {
4571             const SMDS_MeshElement* invElem = it->next();
4572             if ( invElem != elem && !theElems.count( invElem )) continue;
4573             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4574             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4575               needMediumNodes = true;
4576           }
4577         }
4578
4579         // make new nodes
4580         const SMDS_MeshNode * newNode = node;
4581         for ( int i = 0; i < theNbSteps; i++ ) {
4582           if ( !isOnAxis ) {
4583             if ( needMediumNodes )  // create a medium node
4584             {
4585               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4586               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4587               myLastCreatedNodes.Append(newNode);
4588               srcNodes.Append( node );
4589               listNewNodes.push_back( newNode );
4590               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4591             }
4592             else {
4593               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4594             }
4595             // create a corner node
4596             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4597             myLastCreatedNodes.Append(newNode);
4598             srcNodes.Append( node );
4599             listNewNodes.push_back( newNode );
4600           }
4601           else {
4602             listNewNodes.push_back( newNode );
4603             // if ( needMediumNodes )
4604             //   listNewNodes.push_back( newNode );
4605           }
4606         }
4607       }
4608       newNodesItVec.push_back( nIt );
4609     }
4610     // make new elements
4611     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4612   }
4613
4614   if ( theMakeWalls )
4615     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4616
4617   PGroupIDs newGroupIDs;
4618   if ( theMakeGroups )
4619     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4620
4621   return newGroupIDs;
4622 }
4623
4624
4625 //=======================================================================
4626 //function : CreateNode
4627 //purpose  :
4628 //=======================================================================
4629 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4630                                                   const double y,
4631                                                   const double z,
4632                                                   const double tolnode,
4633                                                   SMESH_SequenceOfNode& aNodes)
4634 {
4635   // myLastCreatedElems.Clear();
4636   // myLastCreatedNodes.Clear();
4637
4638   gp_Pnt P1(x,y,z);
4639   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4640
4641   // try to search in sequence of existing nodes
4642   // if aNodes.Length()>0 we 'nave to use given sequence
4643   // else - use all nodes of mesh
4644   if(aNodes.Length()>0) {
4645     int i;
4646     for(i=1; i<=aNodes.Length(); i++) {
4647       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4648       if(P1.Distance(P2)<tolnode)
4649         return aNodes.Value(i);
4650     }
4651   }
4652   else {
4653     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4654     while(itn->more()) {
4655       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4656       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4657       if(P1.Distance(P2)<tolnode)
4658         return aN;
4659     }
4660   }
4661
4662   // create new node and return it
4663   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4664   //myLastCreatedNodes.Append(NewNode);
4665   return NewNode;
4666 }
4667
4668
4669 //=======================================================================
4670 //function : ExtrusionSweep
4671 //purpose  :
4672 //=======================================================================
4673
4674 SMESH_MeshEditor::PGroupIDs
4675 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4676                                   const gp_Vec&       theStep,
4677                                   const int           theNbSteps,
4678                                   TElemOfElemListMap& newElemsMap,
4679                                   const bool          theMakeGroups,
4680                                   const int           theFlags,
4681                                   const double        theTolerance)
4682 {
4683   ExtrusParam aParams;
4684   aParams.myDir = gp_Dir(theStep);
4685   aParams.myNodes.Clear();
4686   aParams.mySteps = new TColStd_HSequenceOfReal;
4687   int i;
4688   for(i=1; i<=theNbSteps; i++)
4689     aParams.mySteps->Append(theStep.Magnitude());
4690
4691   return
4692     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4693 }
4694
4695
4696 //=======================================================================
4697 //function : ExtrusionSweep
4698 //purpose  :
4699 //=======================================================================
4700
4701 SMESH_MeshEditor::PGroupIDs
4702 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4703                                   ExtrusParam&        theParams,
4704                                   TElemOfElemListMap& newElemsMap,
4705                                   const bool          theMakeGroups,
4706                                   const int           theFlags,
4707                                   const double        theTolerance)
4708 {
4709   myLastCreatedElems.Clear();
4710   myLastCreatedNodes.Clear();
4711
4712   // source elements for each generated one
4713   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4714
4715   SMESHDS_Mesh* aMesh = GetMeshDS();
4716
4717   int nbsteps = theParams.mySteps->Length();
4718
4719   TNodeOfNodeListMap mapNewNodes;
4720   //TNodeOfNodeVecMap mapNewNodes;
4721   TElemOfVecOfNnlmiMap mapElemNewNodes;
4722   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4723
4724   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4725                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4726                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4727   // loop on theElems
4728   TIDSortedElemSet::iterator itElem;
4729   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4730     // check element type
4731     const SMDS_MeshElement* elem = *itElem;
4732     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4733       continue;
4734
4735     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4736     newNodesItVec.reserve( elem->NbNodes() );
4737
4738     // loop on elem nodes
4739     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4740     while ( itN->more() )
4741     {
4742       // check if a node has been already sweeped
4743       const SMDS_MeshNode* node = cast2Node( itN->next() );
4744       TNodeOfNodeListMap::iterator nIt =
4745         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4746       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4747       if ( listNewNodes.empty() )
4748       {
4749         // make new nodes
4750
4751         // check if we are to create medium nodes between corner ones
4752         bool needMediumNodes = false;
4753         if ( isQuadraticMesh )
4754         {
4755           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4756           while (it->more() && !needMediumNodes )
4757           {
4758             const SMDS_MeshElement* invElem = it->next();
4759             if ( invElem != elem && !theElems.count( invElem )) continue;
4760             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4761             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4762               needMediumNodes = true;
4763           }
4764         }
4765
4766         double coord[] = { node->X(), node->Y(), node->Z() };
4767         for ( int i = 0; i < nbsteps; i++ )
4768         {
4769           if ( needMediumNodes ) // create a medium node
4770           {
4771             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4772             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4773             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4774             if( theFlags & EXTRUSION_FLAG_SEW ) {
4775               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4776                                                          theTolerance, theParams.myNodes);
4777               listNewNodes.push_back( newNode );
4778             }
4779             else {
4780               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4781               myLastCreatedNodes.Append(newNode);
4782               srcNodes.Append( node );
4783               listNewNodes.push_back( newNode );
4784             }
4785           }
4786           // create a corner node
4787           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4788           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4789           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4790           if( theFlags & EXTRUSION_FLAG_SEW ) {
4791             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4792                                                        theTolerance, theParams.myNodes);
4793             listNewNodes.push_back( newNode );
4794           }
4795           else {
4796             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4797             myLastCreatedNodes.Append(newNode);
4798             srcNodes.Append( node );
4799             listNewNodes.push_back( newNode );
4800           }
4801         }
4802       }
4803       newNodesItVec.push_back( nIt );
4804     }
4805     // make new elements
4806     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4807   }
4808
4809   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4810     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4811   }
4812   PGroupIDs newGroupIDs;
4813   if ( theMakeGroups )
4814     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4815
4816   return newGroupIDs;
4817 }
4818
4819 //=======================================================================
4820 //function : ExtrusionAlongTrack
4821 //purpose  :
4822 //=======================================================================
4823 SMESH_MeshEditor::Extrusion_Error
4824 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4825                                        SMESH_subMesh*       theTrack,
4826                                        const SMDS_MeshNode* theN1,
4827                                        const bool           theHasAngles,
4828                                        list<double>&        theAngles,
4829                                        const bool           theLinearVariation,
4830                                        const bool           theHasRefPoint,
4831                                        const gp_Pnt&        theRefPoint,
4832                                        const bool           theMakeGroups)
4833 {
4834   MESSAGE("ExtrusionAlongTrack");
4835   myLastCreatedElems.Clear();
4836   myLastCreatedNodes.Clear();
4837
4838   int aNbE;
4839   std::list<double> aPrms;
4840   TIDSortedElemSet::iterator itElem;
4841
4842   gp_XYZ aGC;
4843   TopoDS_Edge aTrackEdge;
4844   TopoDS_Vertex aV1, aV2;
4845
4846   SMDS_ElemIteratorPtr aItE;
4847   SMDS_NodeIteratorPtr aItN;
4848   SMDSAbs_ElementType aTypeE;
4849
4850   TNodeOfNodeListMap mapNewNodes;
4851
4852   // 1. Check data
4853   aNbE = theElements.size();
4854   // nothing to do
4855   if ( !aNbE )
4856     return EXTR_NO_ELEMENTS;
4857
4858   // 1.1 Track Pattern
4859   ASSERT( theTrack );
4860
4861   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4862
4863   aItE = pSubMeshDS->GetElements();
4864   while ( aItE->more() ) {
4865     const SMDS_MeshElement* pE = aItE->next();
4866     aTypeE = pE->GetType();
4867     // Pattern must contain links only
4868     if ( aTypeE != SMDSAbs_Edge )
4869       return EXTR_PATH_NOT_EDGE;
4870   }
4871
4872   list<SMESH_MeshEditor_PathPoint> fullList;
4873
4874   const TopoDS_Shape& aS = theTrack->GetSubShape();
4875   // Sub-shape for the Pattern must be an Edge or Wire
4876   if( aS.ShapeType() == TopAbs_EDGE ) {
4877     aTrackEdge = TopoDS::Edge( aS );
4878     // the Edge must not be degenerated
4879     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4880       return EXTR_BAD_PATH_SHAPE;
4881     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4882     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4883     const SMDS_MeshNode* aN1 = aItN->next();
4884     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4885     const SMDS_MeshNode* aN2 = aItN->next();
4886     // starting node must be aN1 or aN2
4887     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4888       return EXTR_BAD_STARTING_NODE;
4889     aItN = pSubMeshDS->GetNodes();
4890     while ( aItN->more() ) {
4891       const SMDS_MeshNode* pNode = aItN->next();
4892       const SMDS_EdgePosition* pEPos =
4893         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4894       double aT = pEPos->GetUParameter();
4895       aPrms.push_back( aT );
4896     }
4897     //Extrusion_Error err =
4898     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4899   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4900     list< SMESH_subMesh* > LSM;
4901     TopTools_SequenceOfShape Edges;
4902     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4903     while(itSM->more()) {
4904       SMESH_subMesh* SM = itSM->next();
4905       LSM.push_back(SM);
4906       const TopoDS_Shape& aS = SM->GetSubShape();
4907       Edges.Append(aS);
4908     }
4909     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4910     int startNid = theN1->GetID();
4911     TColStd_MapOfInteger UsedNums;
4912
4913     int NbEdges = Edges.Length();
4914     int i = 1;
4915     for(; i<=NbEdges; i++) {
4916       int k = 0;
4917       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4918       for(; itLSM!=LSM.end(); itLSM++) {
4919         k++;
4920         if(UsedNums.Contains(k)) continue;
4921         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4922         SMESH_subMesh* locTrack = *itLSM;
4923         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4924         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4925         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4926         const SMDS_MeshNode* aN1 = aItN->next();
4927         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4928         const SMDS_MeshNode* aN2 = aItN->next();
4929         // starting node must be aN1 or aN2
4930         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4931         // 2. Collect parameters on the track edge
4932         aPrms.clear();
4933         aItN = locMeshDS->GetNodes();
4934         while ( aItN->more() ) {
4935           const SMDS_MeshNode* pNode = aItN->next();
4936           const SMDS_EdgePosition* pEPos =
4937             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4938           double aT = pEPos->GetUParameter();
4939           aPrms.push_back( aT );
4940         }
4941         list<SMESH_MeshEditor_PathPoint> LPP;
4942         //Extrusion_Error err =
4943         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4944         LLPPs.push_back(LPP);
4945         UsedNums.Add(k);
4946         // update startN for search following egde
4947         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4948         else startNid = aN1->GetID();
4949         break;
4950       }
4951     }
4952     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4953     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4954     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4955     for(; itPP!=firstList.end(); itPP++) {
4956       fullList.push_back( *itPP );
4957     }
4958     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4959     fullList.pop_back();
4960     itLLPP++;
4961     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4962       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4963       itPP = currList.begin();
4964       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4965       gp_Dir D1 = PP1.Tangent();
4966       gp_Dir D2 = PP2.Tangent();
4967       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4968                            (D1.Z()+D2.Z())/2 ) );
4969       PP1.SetTangent(Dnew);
4970       fullList.push_back(PP1);
4971       itPP++;
4972       for(; itPP!=firstList.end(); itPP++) {
4973         fullList.push_back( *itPP );
4974       }
4975       PP1 = fullList.back();
4976       fullList.pop_back();
4977     }
4978     // if wire not closed
4979     fullList.push_back(PP1);
4980     // else ???
4981   }
4982   else {
4983     return EXTR_BAD_PATH_SHAPE;
4984   }
4985
4986   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4987                           theHasRefPoint, theRefPoint, theMakeGroups);
4988 }
4989
4990
4991 //=======================================================================
4992 //function : ExtrusionAlongTrack
4993 //purpose  :
4994 //=======================================================================
4995 SMESH_MeshEditor::Extrusion_Error
4996 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4997                                        SMESH_Mesh*          theTrack,
4998                                        const SMDS_MeshNode* theN1,
4999                                        const bool           theHasAngles,
5000                                        list<double>&        theAngles,
5001                                        const bool           theLinearVariation,
5002                                        const bool           theHasRefPoint,
5003                                        const gp_Pnt&        theRefPoint,
5004                                        const bool           theMakeGroups)
5005 {
5006   myLastCreatedElems.Clear();
5007   myLastCreatedNodes.Clear();
5008
5009   int aNbE;
5010   std::list<double> aPrms;
5011   TIDSortedElemSet::iterator itElem;
5012
5013   gp_XYZ aGC;
5014   TopoDS_Edge aTrackEdge;
5015   TopoDS_Vertex aV1, aV2;
5016
5017   SMDS_ElemIteratorPtr aItE;
5018   SMDS_NodeIteratorPtr aItN;
5019   SMDSAbs_ElementType aTypeE;
5020
5021   TNodeOfNodeListMap mapNewNodes;
5022
5023   // 1. Check data
5024   aNbE = theElements.size();
5025   // nothing to do
5026   if ( !aNbE )
5027     return EXTR_NO_ELEMENTS;
5028
5029   // 1.1 Track Pattern
5030   ASSERT( theTrack );
5031
5032   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5033
5034   aItE = pMeshDS->elementsIterator();
5035   while ( aItE->more() ) {
5036     const SMDS_MeshElement* pE = aItE->next();
5037     aTypeE = pE->GetType();
5038     // Pattern must contain links only
5039     if ( aTypeE != SMDSAbs_Edge )
5040       return EXTR_PATH_NOT_EDGE;
5041   }
5042
5043   list<SMESH_MeshEditor_PathPoint> fullList;
5044
5045   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5046
5047   if( aS == SMESH_Mesh::PseudoShape() ) {
5048     //Mesh without shape
5049     const SMDS_MeshNode* currentNode = NULL;
5050     const SMDS_MeshNode* prevNode = theN1;
5051     std::vector<const SMDS_MeshNode*> aNodesList;
5052     aNodesList.push_back(theN1);
5053     int nbEdges = 0, conn=0;
5054     const SMDS_MeshElement* prevElem = NULL;
5055     const SMDS_MeshElement* currentElem = NULL;
5056     int totalNbEdges = theTrack->NbEdges();
5057     SMDS_ElemIteratorPtr nIt;
5058
5059     //check start node
5060     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5061       return EXTR_BAD_STARTING_NODE;
5062     }
5063
5064     conn = nbEdgeConnectivity(theN1);
5065     if(conn > 2)
5066       return EXTR_PATH_NOT_EDGE;
5067
5068     aItE = theN1->GetInverseElementIterator();
5069     prevElem = aItE->next();
5070     currentElem = prevElem;
5071     //Get all nodes
5072     if(totalNbEdges == 1 ) {
5073       nIt = currentElem->nodesIterator();
5074       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5075       if(currentNode == prevNode)
5076         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5077       aNodesList.push_back(currentNode);
5078     } else {
5079       nIt = currentElem->nodesIterator();
5080       while( nIt->more() ) {
5081         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5082         if(currentNode == prevNode)
5083           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5084         aNodesList.push_back(currentNode);
5085
5086         //case of the closed mesh
5087         if(currentNode == theN1) {
5088           nbEdges++;
5089           break;
5090         }
5091
5092         conn = nbEdgeConnectivity(currentNode);
5093         if(conn > 2) {
5094           return EXTR_PATH_NOT_EDGE;
5095         }else if( conn == 1 && nbEdges > 0 ) {
5096           //End of the path
5097           nbEdges++;
5098           break;
5099         }else {
5100           prevNode = currentNode;
5101           aItE = currentNode->GetInverseElementIterator();
5102           currentElem = aItE->next();
5103           if( currentElem  == prevElem)
5104             currentElem = aItE->next();
5105           nIt = currentElem->nodesIterator();
5106           prevElem = currentElem;
5107           nbEdges++;
5108         }
5109       }
5110     }
5111
5112     if(nbEdges != totalNbEdges)
5113       return EXTR_PATH_NOT_EDGE;
5114
5115     TopTools_SequenceOfShape Edges;
5116     double x1,x2,y1,y2,z1,z2;
5117     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5118     int startNid = theN1->GetID();
5119     for(int i = 1; i < aNodesList.size(); i++) {
5120       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5121       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5122       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5123       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5124       list<SMESH_MeshEditor_PathPoint> LPP;
5125       aPrms.clear();
5126       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5127       LLPPs.push_back(LPP);
5128       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5129       else startNid = aNodesList[i-1]->GetID();
5130
5131     }
5132
5133     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5134     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5135     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5136     for(; itPP!=firstList.end(); itPP++) {
5137       fullList.push_back( *itPP );
5138     }
5139
5140     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5141     SMESH_MeshEditor_PathPoint PP2;
5142     fullList.pop_back();
5143     itLLPP++;
5144     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5145       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5146       itPP = currList.begin();
5147       PP2 = currList.front();
5148       gp_Dir D1 = PP1.Tangent();
5149       gp_Dir D2 = PP2.Tangent();
5150       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5151                            (D1.Z()+D2.Z())/2 ) );
5152       PP1.SetTangent(Dnew);
5153       fullList.push_back(PP1);
5154       itPP++;
5155       for(; itPP!=currList.end(); itPP++) {
5156         fullList.push_back( *itPP );
5157       }
5158       PP1 = fullList.back();
5159       fullList.pop_back();
5160     }
5161     fullList.push_back(PP1);
5162
5163   } // Sub-shape for the Pattern must be an Edge or Wire
5164   else if( aS.ShapeType() == TopAbs_EDGE ) {
5165     aTrackEdge = TopoDS::Edge( aS );
5166     // the Edge must not be degenerated
5167     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5168       return EXTR_BAD_PATH_SHAPE;
5169     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5170     aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5171     const SMDS_MeshNode* aN1 = aItN->next();
5172     aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5173     const SMDS_MeshNode* aN2 = aItN->next();
5174     // starting node must be aN1 or aN2
5175     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5176       return EXTR_BAD_STARTING_NODE;
5177     aItN = pMeshDS->nodesIterator();
5178     while ( aItN->more() ) {
5179       const SMDS_MeshNode* pNode = aItN->next();
5180       if( pNode==aN1 || pNode==aN2 ) continue;
5181       const SMDS_EdgePosition* pEPos =
5182         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5183       double aT = pEPos->GetUParameter();
5184       aPrms.push_back( aT );
5185     }
5186     //Extrusion_Error err =
5187     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5188   }
5189   else if( aS.ShapeType() == TopAbs_WIRE ) {
5190     list< SMESH_subMesh* > LSM;
5191     TopTools_SequenceOfShape Edges;
5192     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5193     for(; eExp.More(); eExp.Next()) {
5194       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5195       if( BRep_Tool::Degenerated(E) ) continue;
5196       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5197       if(SM) {
5198         LSM.push_back(SM);
5199         Edges.Append(E);
5200       }
5201     }
5202     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5203     int startNid = theN1->GetID();
5204     TColStd_MapOfInteger UsedNums;
5205     int NbEdges = Edges.Length();
5206     int i = 1;
5207     for(; i<=NbEdges; i++) {
5208       int k = 0;
5209       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5210       for(; itLSM!=LSM.end(); itLSM++) {
5211         k++;
5212         if(UsedNums.Contains(k)) continue;
5213         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5214         SMESH_subMesh* locTrack = *itLSM;
5215         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5216         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5217         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5218         const SMDS_MeshNode* aN1 = aItN->next();
5219         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5220         const SMDS_MeshNode* aN2 = aItN->next();
5221         // starting node must be aN1 or aN2
5222         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5223         // 2. Collect parameters on the track edge
5224         aPrms.clear();
5225         aItN = locMeshDS->GetNodes();
5226         while ( aItN->more() ) {
5227           const SMDS_MeshNode* pNode = aItN->next();
5228           const SMDS_EdgePosition* pEPos =
5229             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5230           double aT = pEPos->GetUParameter();
5231           aPrms.push_back( aT );
5232         }
5233         list<SMESH_MeshEditor_PathPoint> LPP;
5234         //Extrusion_Error err =
5235         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5236         LLPPs.push_back(LPP);
5237         UsedNums.Add(k);
5238         // update startN for search following egde
5239         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5240         else startNid = aN1->GetID();
5241         break;
5242       }
5243     }
5244     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5245     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5246     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5247     for(; itPP!=firstList.end(); itPP++) {
5248       fullList.push_back( *itPP );
5249     }
5250     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5251     fullList.pop_back();
5252     itLLPP++;
5253     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5254       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5255       itPP = currList.begin();
5256       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5257       gp_Dir D1 = PP1.Tangent();
5258       gp_Dir D2 = PP2.Tangent();
5259       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5260                            (D1.Z()+D2.Z())/2 ) );
5261       PP1.SetTangent(Dnew);
5262       fullList.push_back(PP1);
5263       itPP++;
5264       for(; itPP!=currList.end(); itPP++) {
5265         fullList.push_back( *itPP );
5266       }
5267       PP1 = fullList.back();
5268       fullList.pop_back();
5269     }
5270     // if wire not closed
5271     fullList.push_back(PP1);
5272     // else ???
5273   }
5274   else {
5275     return EXTR_BAD_PATH_SHAPE;
5276   }
5277
5278   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5279                           theHasRefPoint, theRefPoint, theMakeGroups);
5280 }
5281
5282
5283 //=======================================================================
5284 //function : MakeEdgePathPoints
5285 //purpose  : auxilary for ExtrusionAlongTrack
5286 //=======================================================================
5287 SMESH_MeshEditor::Extrusion_Error
5288 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5289                                      const TopoDS_Edge& aTrackEdge,
5290                                      bool FirstIsStart,
5291                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5292 {
5293   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5294   aTolVec=1.e-7;
5295   aTolVec2=aTolVec*aTolVec;
5296   double aT1, aT2;
5297   TopoDS_Vertex aV1, aV2;
5298   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5299   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5300   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5301   // 2. Collect parameters on the track edge
5302   aPrms.push_front( aT1 );
5303   aPrms.push_back( aT2 );
5304   // sort parameters
5305   aPrms.sort();
5306   if( FirstIsStart ) {
5307     if ( aT1 > aT2 ) {
5308       aPrms.reverse();
5309     }
5310   }
5311   else {
5312     if ( aT2 > aT1 ) {
5313       aPrms.reverse();
5314     }
5315   }
5316   // 3. Path Points
5317   SMESH_MeshEditor_PathPoint aPP;
5318   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5319   std::list<double>::iterator aItD = aPrms.begin();
5320   for(; aItD != aPrms.end(); ++aItD) {
5321     double aT = *aItD;
5322     gp_Pnt aP3D;
5323     gp_Vec aVec;
5324     aC3D->D1( aT, aP3D, aVec );
5325     aL2 = aVec.SquareMagnitude();
5326     if ( aL2 < aTolVec2 )
5327       return EXTR_CANT_GET_TANGENT;
5328     gp_Dir aTgt( aVec );
5329     aPP.SetPnt( aP3D );
5330     aPP.SetTangent( aTgt );
5331     aPP.SetParameter( aT );
5332     LPP.push_back(aPP);
5333   }
5334   return EXTR_OK;
5335 }
5336
5337
5338 //=======================================================================
5339 //function : MakeExtrElements
5340 //purpose  : auxilary for ExtrusionAlongTrack
5341 //=======================================================================
5342 SMESH_MeshEditor::Extrusion_Error
5343 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5344                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5345                                    const bool theHasAngles,
5346                                    list<double>& theAngles,
5347                                    const bool theLinearVariation,
5348                                    const bool theHasRefPoint,
5349                                    const gp_Pnt& theRefPoint,
5350                                    const bool theMakeGroups)
5351 {
5352   MESSAGE("MakeExtrElements");
5353   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5354   int aNbTP = fullList.size();
5355   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5356   // Angles
5357   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5358     LinearAngleVariation(aNbTP-1, theAngles);
5359   }
5360   vector<double> aAngles( aNbTP );
5361   int j = 0;
5362   for(; j<aNbTP; ++j) {
5363     aAngles[j] = 0.;
5364   }
5365   if ( theHasAngles ) {
5366     double anAngle;;
5367     std::list<double>::iterator aItD = theAngles.begin();
5368     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5369       anAngle = *aItD;
5370       aAngles[j] = anAngle;
5371     }
5372   }
5373   // fill vector of path points with angles
5374   //aPPs.resize(fullList.size());
5375   j = -1;
5376   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5377   for(; itPP!=fullList.end(); itPP++) {
5378     j++;
5379     SMESH_MeshEditor_PathPoint PP = *itPP;
5380     PP.SetAngle(aAngles[j]);
5381     aPPs[j] = PP;
5382   }
5383
5384   TNodeOfNodeListMap mapNewNodes;
5385   TElemOfVecOfNnlmiMap mapElemNewNodes;
5386   TElemOfElemListMap newElemsMap;
5387   TIDSortedElemSet::iterator itElem;
5388   double aX, aY, aZ;
5389   int aNb;
5390   SMDSAbs_ElementType aTypeE;
5391   // source elements for each generated one
5392   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5393
5394   // 3. Center of rotation aV0
5395   gp_Pnt aV0 = theRefPoint;
5396   gp_XYZ aGC;
5397   if ( !theHasRefPoint ) {
5398     aNb = 0;
5399     aGC.SetCoord( 0.,0.,0. );
5400
5401     itElem = theElements.begin();
5402     for ( ; itElem != theElements.end(); itElem++ ) {
5403       const SMDS_MeshElement* elem = *itElem;
5404
5405       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5406       while ( itN->more() ) {
5407         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5408         aX = node->X();
5409         aY = node->Y();
5410         aZ = node->Z();
5411
5412         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5413           list<const SMDS_MeshNode*> aLNx;
5414           mapNewNodes[node] = aLNx;
5415           //
5416           gp_XYZ aXYZ( aX, aY, aZ );
5417           aGC += aXYZ;
5418           ++aNb;
5419         }
5420       }
5421     }
5422     aGC /= aNb;
5423     aV0.SetXYZ( aGC );
5424   } // if (!theHasRefPoint) {
5425   mapNewNodes.clear();
5426
5427   // 4. Processing the elements
5428   SMESHDS_Mesh* aMesh = GetMeshDS();
5429
5430   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5431     // check element type
5432     const SMDS_MeshElement* elem = *itElem;
5433     aTypeE = elem->GetType();
5434     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5435       continue;
5436
5437     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5438     newNodesItVec.reserve( elem->NbNodes() );
5439
5440     // loop on elem nodes
5441     int nodeIndex = -1;
5442     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5443     while ( itN->more() )
5444     {
5445       ++nodeIndex;
5446       // check if a node has been already processed
5447       const SMDS_MeshNode* node =
5448         static_cast<const SMDS_MeshNode*>( itN->next() );
5449       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5450       if ( nIt == mapNewNodes.end() ) {
5451         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5452         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5453
5454         // make new nodes
5455         aX = node->X();  aY = node->Y(); aZ = node->Z();
5456
5457         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5458         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5459         gp_Ax1 anAx1, anAxT1T0;
5460         gp_Dir aDT1x, aDT0x, aDT1T0;
5461
5462         aTolAng=1.e-4;
5463
5464         aV0x = aV0;
5465         aPN0.SetCoord(aX, aY, aZ);
5466
5467         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5468         aP0x = aPP0.Pnt();
5469         aDT0x= aPP0.Tangent();
5470         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5471
5472         for ( j = 1; j < aNbTP; ++j ) {
5473           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5474           aP1x = aPP1.Pnt();
5475           aDT1x = aPP1.Tangent();
5476           aAngle1x = aPP1.Angle();
5477
5478           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5479           // Translation
5480           gp_Vec aV01x( aP0x, aP1x );
5481           aTrsf.SetTranslation( aV01x );
5482
5483           // traslated point
5484           aV1x = aV0x.Transformed( aTrsf );
5485           aPN1 = aPN0.Transformed( aTrsf );
5486
5487           // rotation 1 [ T1,T0 ]
5488           aAngleT1T0=-aDT1x.Angle( aDT0x );
5489           if (fabs(aAngleT1T0) > aTolAng) {
5490             aDT1T0=aDT1x^aDT0x;
5491             anAxT1T0.SetLocation( aV1x );
5492             anAxT1T0.SetDirection( aDT1T0 );
5493             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5494
5495             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5496           }
5497
5498           // rotation 2
5499           if ( theHasAngles ) {
5500             anAx1.SetLocation( aV1x );
5501             anAx1.SetDirection( aDT1x );
5502             aTrsfRot.SetRotation( anAx1, aAngle1x );
5503
5504             aPN1 = aPN1.Transformed( aTrsfRot );
5505           }
5506
5507           // make new node
5508           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5509           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5510             // create additional node
5511             double x = ( aPN1.X() + aPN0.X() )/2.;
5512             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5513             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5514             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5515             myLastCreatedNodes.Append(newNode);
5516             srcNodes.Append( node );
5517             listNewNodes.push_back( newNode );
5518           }
5519           aX = aPN1.X();
5520           aY = aPN1.Y();
5521           aZ = aPN1.Z();
5522           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5523           myLastCreatedNodes.Append(newNode);
5524           srcNodes.Append( node );
5525           listNewNodes.push_back( newNode );
5526
5527           aPN0 = aPN1;
5528           aP0x = aP1x;
5529           aV0x = aV1x;
5530           aDT0x = aDT1x;
5531         }
5532       }
5533
5534       else {
5535         // if current elem is quadratic and current node is not medium
5536         // we have to check - may be it is needed to insert additional nodes
5537         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5538           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5539           if(listNewNodes.size()==aNbTP-1) {
5540             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5541             gp_XYZ P(node->X(), node->Y(), node->Z());
5542             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5543             int i;
5544             for(i=0; i<aNbTP-1; i++) {
5545               const SMDS_MeshNode* N = *it;
5546               double x = ( N->X() + P.X() )/2.;
5547               double y = ( N->Y() + P.Y() )/2.;
5548               double z = ( N->Z() + P.Z() )/2.;
5549               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5550               srcNodes.Append( node );
5551               myLastCreatedNodes.Append(newN);
5552               aNodes[2*i] = newN;
5553               aNodes[2*i+1] = N;
5554               P = gp_XYZ(N->X(),N->Y(),N->Z());
5555             }
5556             listNewNodes.clear();
5557             for(i=0; i<2*(aNbTP-1); i++) {
5558               listNewNodes.push_back(aNodes[i]);
5559             }
5560           }
5561         }
5562       }
5563
5564       newNodesItVec.push_back( nIt );
5565     }
5566     // make new elements
5567     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5568     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5569     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5570   }
5571
5572   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5573
5574   if ( theMakeGroups )
5575     generateGroups( srcNodes, srcElems, "extruded");
5576
5577   return EXTR_OK;
5578 }
5579
5580
5581 //=======================================================================
5582 //function : LinearAngleVariation
5583 //purpose  : auxilary for ExtrusionAlongTrack
5584 //=======================================================================
5585 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5586                                             list<double>& Angles)
5587 {
5588   int nbAngles = Angles.size();
5589   if( nbSteps > nbAngles ) {
5590     vector<double> theAngles(nbAngles);
5591     list<double>::iterator it = Angles.begin();
5592     int i = -1;
5593     for(; it!=Angles.end(); it++) {
5594       i++;
5595       theAngles[i] = (*it);
5596     }
5597     list<double> res;
5598     double rAn2St = double( nbAngles ) / double( nbSteps );
5599     double angPrev = 0, angle;
5600     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5601       double angCur = rAn2St * ( iSt+1 );
5602       double angCurFloor  = floor( angCur );
5603       double angPrevFloor = floor( angPrev );
5604       if ( angPrevFloor == angCurFloor )
5605         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5606       else {
5607         int iP = int( angPrevFloor );
5608         double angPrevCeil = ceil(angPrev);
5609         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5610
5611         int iC = int( angCurFloor );
5612         if ( iC < nbAngles )
5613           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5614
5615         iP = int( angPrevCeil );
5616         while ( iC-- > iP )
5617           angle += theAngles[ iC ];
5618       }
5619       res.push_back(angle);
5620       angPrev = angCur;
5621     }
5622     Angles.clear();
5623     it = res.begin();
5624     for(; it!=res.end(); it++)
5625       Angles.push_back( *it );
5626   }
5627 }
5628
5629
5630 //================================================================================
5631 /*!
5632  * \brief Move or copy theElements applying theTrsf to their nodes
5633  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5634  *  \param theTrsf - transformation to apply
5635  *  \param theCopy - if true, create translated copies of theElems
5636  *  \param theMakeGroups - if true and theCopy, create translated groups
5637  *  \param theTargetMesh - mesh to copy translated elements into
5638  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5639  */
5640 //================================================================================
5641
5642 SMESH_MeshEditor::PGroupIDs
5643 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5644                              const gp_Trsf&     theTrsf,
5645                              const bool         theCopy,
5646                              const bool         theMakeGroups,
5647                              SMESH_Mesh*        theTargetMesh)
5648 {
5649   myLastCreatedElems.Clear();
5650   myLastCreatedNodes.Clear();
5651
5652   bool needReverse = false;
5653   string groupPostfix;
5654   switch ( theTrsf.Form() ) {
5655   case gp_PntMirror:
5656     MESSAGE("gp_PntMirror");
5657     needReverse = true;
5658     groupPostfix = "mirrored";
5659     break;
5660   case gp_Ax1Mirror:
5661     MESSAGE("gp_Ax1Mirror");
5662     groupPostfix = "mirrored";
5663     break;
5664   case gp_Ax2Mirror:
5665     MESSAGE("gp_Ax2Mirror");
5666     needReverse = true;
5667     groupPostfix = "mirrored";
5668     break;
5669   case gp_Rotation:
5670     MESSAGE("gp_Rotation");
5671     groupPostfix = "rotated";
5672     break;
5673   case gp_Translation:
5674     MESSAGE("gp_Translation");
5675     groupPostfix = "translated";
5676     break;
5677   case gp_Scale:
5678     MESSAGE("gp_Scale");
5679     groupPostfix = "scaled";
5680     break;
5681   case gp_CompoundTrsf: // different scale by axis
5682     MESSAGE("gp_CompoundTrsf");
5683     groupPostfix = "scaled";
5684     break;
5685   default:
5686     MESSAGE("default");
5687     needReverse = false;
5688     groupPostfix = "transformed";
5689   }
5690
5691   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5692   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5693   SMESHDS_Mesh* aMesh    = GetMeshDS();
5694
5695
5696   // map old node to new one
5697   TNodeNodeMap nodeMap;
5698
5699   // elements sharing moved nodes; those of them which have all
5700   // nodes mirrored but are not in theElems are to be reversed
5701   TIDSortedElemSet inverseElemSet;
5702
5703   // source elements for each generated one
5704   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5705
5706   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5707   TIDSortedElemSet orphanNode;
5708
5709   if ( theElems.empty() ) // transform the whole mesh
5710   {
5711     // add all elements
5712     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5713     while ( eIt->more() ) theElems.insert( eIt->next() );
5714     // add orphan nodes
5715     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5716     while ( nIt->more() )
5717     {
5718       const SMDS_MeshNode* node = nIt->next();
5719       if ( node->NbInverseElements() == 0)
5720         orphanNode.insert( node );
5721     }
5722   }
5723
5724   // loop on elements to transform nodes : first orphan nodes then elems
5725   TIDSortedElemSet::iterator itElem;
5726   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5727   for (int i=0; i<2; i++)
5728   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5729     const SMDS_MeshElement* elem = *itElem;
5730     if ( !elem )
5731       continue;
5732
5733     // loop on elem nodes
5734     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5735     while ( itN->more() ) {
5736
5737       const SMDS_MeshNode* node = cast2Node( itN->next() );
5738       // check if a node has been already transformed
5739       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5740         nodeMap.insert( make_pair ( node, node ));
5741       if ( !n2n_isnew.second )
5742         continue;
5743
5744       double coord[3];
5745       coord[0] = node->X();
5746       coord[1] = node->Y();
5747       coord[2] = node->Z();
5748       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5749       if ( theTargetMesh ) {
5750         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5751         n2n_isnew.first->second = newNode;
5752         myLastCreatedNodes.Append(newNode);
5753         srcNodes.Append( node );
5754       }
5755       else if ( theCopy ) {
5756         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5757         n2n_isnew.first->second = newNode;
5758         myLastCreatedNodes.Append(newNode);
5759         srcNodes.Append( node );
5760       }
5761       else {
5762         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5763         // node position on shape becomes invalid
5764         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5765           ( SMDS_SpacePosition::originSpacePosition() );
5766       }
5767
5768       // keep inverse elements
5769       if ( !theCopy && !theTargetMesh && needReverse ) {
5770         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5771         while ( invElemIt->more() ) {
5772           const SMDS_MeshElement* iel = invElemIt->next();
5773           inverseElemSet.insert( iel );
5774         }
5775       }
5776     }
5777   }
5778
5779   // either create new elements or reverse mirrored ones
5780   if ( !theCopy && !needReverse && !theTargetMesh )
5781     return PGroupIDs();
5782
5783   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5784   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5785     theElems.insert( *invElemIt );
5786
5787   // Replicate or reverse elements
5788
5789   std::vector<int> iForw;
5790   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5791   {
5792     const SMDS_MeshElement* elem = *itElem;
5793     if ( !elem ) continue;
5794
5795     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5796     int                  nbNodes  = elem->NbNodes();
5797     if ( geomType == SMDSGeom_NONE ) continue; // node
5798
5799     switch ( geomType ) {
5800
5801     case SMDSGeom_POLYGON:  // ---------------------- polygon
5802       {
5803         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5804         int iNode = 0;
5805         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5806         while (itN->more()) {
5807           const SMDS_MeshNode* node =
5808             static_cast<const SMDS_MeshNode*>(itN->next());
5809           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5810           if (nodeMapIt == nodeMap.end())
5811             break; // not all nodes transformed
5812           if (needReverse) {
5813             // reverse mirrored faces and volumes
5814             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5815           } else {
5816             poly_nodes[iNode] = (*nodeMapIt).second;
5817           }
5818           iNode++;
5819         }
5820         if ( iNode != nbNodes )
5821           continue; // not all nodes transformed
5822
5823         if ( theTargetMesh ) {
5824           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5825           srcElems.Append( elem );
5826         }
5827         else if ( theCopy ) {
5828           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5829           srcElems.Append( elem );
5830         }
5831         else {
5832           aMesh->ChangePolygonNodes(elem, poly_nodes);
5833         }
5834       }
5835       break;
5836
5837     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5838       {
5839         const SMDS_VtkVolume* aPolyedre =
5840           dynamic_cast<const SMDS_VtkVolume*>( elem );
5841         if (!aPolyedre) {
5842           MESSAGE("Warning: bad volumic element");
5843           continue;
5844         }
5845
5846         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5847         vector<int> quantities; quantities.reserve( nbNodes );
5848
5849         bool allTransformed = true;
5850         int nbFaces = aPolyedre->NbFaces();
5851         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5852           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5853           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5854             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5855             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5856             if (nodeMapIt == nodeMap.end()) {
5857               allTransformed = false; // not all nodes transformed
5858             } else {
5859               poly_nodes.push_back((*nodeMapIt).second);
5860             }
5861             if ( needReverse && allTransformed )
5862               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5863           }
5864           quantities.push_back(nbFaceNodes);
5865         }
5866         if ( !allTransformed )
5867           continue; // not all nodes transformed
5868
5869         if ( theTargetMesh ) {
5870           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5871           srcElems.Append( elem );
5872         }
5873         else if ( theCopy ) {
5874           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5875           srcElems.Append( elem );
5876         }
5877         else {
5878           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5879         }
5880       }
5881       break;
5882
5883     case SMDSGeom_BALL: // -------------------- Ball
5884       {
5885         if ( !theCopy && !theTargetMesh ) continue;
5886
5887         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5888         if (nodeMapIt == nodeMap.end())
5889           continue; // not all nodes transformed
5890
5891         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5892         if ( theTargetMesh ) {
5893           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5894           srcElems.Append( elem );
5895         }
5896         else {
5897           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5898           srcElems.Append( elem );
5899         }
5900       }
5901       break;
5902
5903     default: // ----------------------- Regular elements
5904
5905       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5906       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5907       const std::vector<int>& i = needReverse ? iRev : iForw;
5908
5909       // find transformed nodes
5910       vector<const SMDS_MeshNode*> nodes(nbNodes);
5911       int iNode = 0;
5912       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5913       while ( itN->more() ) {
5914         const SMDS_MeshNode* node =
5915           static_cast<const SMDS_MeshNode*>( itN->next() );
5916         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5917         if ( nodeMapIt == nodeMap.end() )
5918           break; // not all nodes transformed
5919         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5920       }
5921       if ( iNode != nbNodes )
5922         continue; // not all nodes transformed
5923
5924       if ( theTargetMesh ) {
5925         if ( SMDS_MeshElement* copy =
5926              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5927           myLastCreatedElems.Append( copy );
5928           srcElems.Append( elem );
5929         }
5930       }
5931       else if ( theCopy ) {
5932         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5933           srcElems.Append( elem );
5934       }
5935       else {
5936         // reverse element as it was reversed by transformation
5937         if ( nbNodes > 2 )
5938           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5939       }
5940     } // switch ( geomType )
5941
5942   } // loop on elements
5943
5944   PGroupIDs newGroupIDs;
5945
5946   if ( ( theMakeGroups && theCopy ) ||
5947        ( theMakeGroups && theTargetMesh ) )
5948     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5949
5950   return newGroupIDs;
5951 }
5952
5953 //=======================================================================
5954 /*!
5955  * \brief Create groups of elements made during transformation
5956  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5957  * \param elemGens - elements making corresponding myLastCreatedElems
5958  * \param postfix - to append to names of new groups
5959  */
5960 //=======================================================================
5961
5962 SMESH_MeshEditor::PGroupIDs
5963 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5964                                  const SMESH_SequenceOfElemPtr& elemGens,
5965                                  const std::string&             postfix,
5966                                  SMESH_Mesh*                    targetMesh)
5967 {
5968   PGroupIDs newGroupIDs( new list<int> );
5969   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5970
5971   // Sort existing groups by types and collect their names
5972
5973   // to store an old group and a generated new one
5974   typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5975   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5976   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5977   // group names
5978   set< string > groupNames;
5979
5980   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5981   if ( !groupIt->more() ) return newGroupIDs;
5982
5983   int newGroupID = mesh->GetGroupIds().back()+1;
5984   while ( groupIt->more() )
5985   {
5986     SMESH_Group * group = groupIt->next();
5987     if ( !group ) continue;
5988     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5989     if ( !groupDS || groupDS->IsEmpty() ) continue;
5990     groupNames.insert( group->GetName() );
5991     groupDS->SetStoreName( group->GetName() );
5992     SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5993                                                  groupDS->GetType() );
5994     groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5995     orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5996   }
5997
5998   // Loop on nodes and elements to add them in new groups
5999
6000   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6001   {
6002     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6003     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6004     if ( gens.Length() != elems.Length() )
6005       throw SALOME_Exception(LOCALIZED("invalid args"));
6006
6007     // loop on created elements
6008     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6009     {
6010       const SMDS_MeshElement* sourceElem = gens( iElem );
6011       if ( !sourceElem ) {
6012         MESSAGE("generateGroups(): NULL source element");
6013         continue;
6014       }
6015       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6016       if ( groupsOldNew.empty() ) { // no groups of this type at all
6017         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6018           ++iElem; // skip all elements made by sourceElem
6019         continue;
6020       }
6021       // collect all elements made by sourceElem
6022       list< const SMDS_MeshElement* > resultElems;
6023       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6024         if ( resElem != sourceElem )
6025           resultElems.push_back( resElem );
6026       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6027         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6028           if ( resElem != sourceElem )
6029             resultElems.push_back( resElem );
6030
6031       // add resultElems to groups made by ones the sourceElem belongs to
6032       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6033       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6034       {
6035         SMESHDS_GroupBase* oldGroup = gOldNew->first;
6036         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6037         {
6038           // fill in a new group
6039           SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6040           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6041           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6042             newGroup.Add( *resElemIt );
6043         }
6044       }
6045     } // loop on created elements
6046   }// loop on nodes and elements
6047
6048   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6049
6050   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6051   {
6052     SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6053     SMESHDS_Group*     newGroupDS = orderedOldNewGroups[i]->second;
6054     if ( newGroupDS->IsEmpty() )
6055     {
6056       mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6057     }
6058     else
6059     {
6060       // make a name
6061       string name = oldGroupDS->GetStoreName();
6062       if ( !targetMesh ) {
6063         name += "_";
6064         name += postfix;
6065         int nb = 1;
6066         while ( !groupNames.insert( name ).second ) // name exists
6067           name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6068       }
6069       newGroupDS->SetStoreName( name.c_str() );
6070
6071       // make a SMESH_Groups
6072       mesh->AddGroup( newGroupDS );
6073       newGroupIDs->push_back( newGroupDS->GetID() );
6074
6075       // set group type
6076       newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6077     }
6078   }
6079
6080   return newGroupIDs;
6081 }
6082
6083 //================================================================================
6084 /*!
6085  * \brief Return list of group of nodes close to each other within theTolerance
6086  *        Search among theNodes or in the whole mesh if theNodes is empty using
6087  *        an Octree algorithm
6088  */
6089 //================================================================================
6090
6091 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6092                                             const double         theTolerance,
6093                                             TListOfListOfNodes & theGroupsOfNodes)
6094 {
6095   myLastCreatedElems.Clear();
6096   myLastCreatedNodes.Clear();
6097
6098   if ( theNodes.empty() )
6099   { // get all nodes in the mesh
6100     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6101     while ( nIt->more() )
6102       theNodes.insert( theNodes.end(),nIt->next());
6103   }
6104
6105   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6106 }
6107
6108
6109 //=======================================================================
6110 /*!
6111  * \brief Implementation of search for the node closest to point
6112  */
6113 //=======================================================================
6114
6115 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6116 {
6117   //---------------------------------------------------------------------
6118   /*!
6119    * \brief Constructor
6120    */
6121   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6122   {
6123     myMesh = ( SMESHDS_Mesh* ) theMesh;
6124
6125     TIDSortedNodeSet nodes;
6126     if ( theMesh ) {
6127       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6128       while ( nIt->more() )
6129         nodes.insert( nodes.end(), nIt->next() );
6130     }
6131     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6132
6133     // get max size of a leaf box
6134     SMESH_OctreeNode* tree = myOctreeNode;
6135     while ( !tree->isLeaf() )
6136     {
6137       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6138       if ( cIt->more() )
6139         tree = cIt->next();
6140     }
6141     myHalfLeafSize = tree->maxSize() / 2.;
6142   }
6143
6144   //---------------------------------------------------------------------
6145   /*!
6146    * \brief Move node and update myOctreeNode accordingly
6147    */
6148   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6149   {
6150     myOctreeNode->UpdateByMoveNode( node, toPnt );
6151     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6152   }
6153
6154   //---------------------------------------------------------------------
6155   /*!
6156    * \brief Do it's job
6157    */
6158   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6159   {
6160     map<double, const SMDS_MeshNode*> dist2Nodes;
6161     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6162     if ( !dist2Nodes.empty() )
6163       return dist2Nodes.begin()->second;
6164     list<const SMDS_MeshNode*> nodes;
6165     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6166
6167     double minSqDist = DBL_MAX;
6168     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6169     {
6170       // sort leafs by their distance from thePnt
6171       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6172       TDistTreeMap treeMap;
6173       list< SMESH_OctreeNode* > treeList;
6174       list< SMESH_OctreeNode* >::iterator trIt;
6175       treeList.push_back( myOctreeNode );
6176
6177       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6178       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6179       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6180       {
6181         SMESH_OctreeNode* tree = *trIt;
6182         if ( !tree->isLeaf() ) // put children to the queue
6183         {
6184           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6185           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6186           while ( cIt->more() )
6187             treeList.push_back( cIt->next() );
6188         }
6189         else if ( tree->NbNodes() ) // put a tree to the treeMap
6190         {
6191           const Bnd_B3d& box = *tree->getBox();
6192           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6193           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6194           if ( !it_in.second ) // not unique distance to box center
6195             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6196         }
6197       }
6198       // find distance after which there is no sense to check tree's
6199       double sqLimit = DBL_MAX;
6200       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6201       if ( treeMap.size() > 5 ) {
6202         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6203         const Bnd_B3d& box = *closestTree->getBox();
6204         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6205         sqLimit = limit * limit;
6206       }
6207       // get all nodes from trees
6208       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6209         if ( sqDist_tree->first > sqLimit )
6210           break;
6211         SMESH_OctreeNode* tree = sqDist_tree->second;
6212         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6213       }
6214     }
6215     // find closest among nodes
6216     minSqDist = DBL_MAX;
6217     const SMDS_MeshNode* closestNode = 0;
6218     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6219     for ( ; nIt != nodes.end(); ++nIt ) {
6220       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6221       if ( minSqDist > sqDist ) {
6222         closestNode = *nIt;
6223         minSqDist = sqDist;
6224       }
6225     }
6226     return closestNode;
6227   }
6228
6229   //---------------------------------------------------------------------
6230   /*!
6231    * \brief Destructor
6232    */
6233   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6234
6235   //---------------------------------------------------------------------
6236   /*!
6237    * \brief Return the node tree
6238    */
6239   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6240
6241 private:
6242   SMESH_OctreeNode* myOctreeNode;
6243   SMESHDS_Mesh*     myMesh;
6244   double            myHalfLeafSize; // max size of a leaf box
6245 };
6246
6247 //=======================================================================
6248 /*!
6249  * \brief Return SMESH_NodeSearcher
6250  */
6251 //=======================================================================
6252
6253 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6254 {
6255   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6256 }
6257
6258 // ========================================================================
6259 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6260 {
6261   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6262   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6263   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6264
6265   //=======================================================================
6266   /*!
6267    * \brief Octal tree of bounding boxes of elements
6268    */
6269   //=======================================================================
6270
6271   class ElementBndBoxTree : public SMESH_Octree
6272   {
6273   public:
6274
6275     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6276                       SMDSAbs_ElementType  elemType,
6277                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6278                       double               tolerance = NodeRadius );
6279     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6280     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6281     void getElementsInSphere ( const gp_XYZ& center,
6282                                const double  radius, TIDSortedElemSet& foundElems);
6283     size_t getSize() { return std::max( _size, _elements.size() ); }
6284     ~ElementBndBoxTree();
6285
6286   protected:
6287     ElementBndBoxTree():_size(0) {}
6288     SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6289     void          buildChildrenData();
6290     Bnd_B3d*      buildRootBox();
6291   private:
6292     //!< Bounding box of element
6293     struct ElementBox : public Bnd_B3d
6294     {
6295       const SMDS_MeshElement* _element;
6296       int                     _refCount; // an ElementBox can be included in several tree branches
6297       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6298     };
6299     vector< ElementBox* > _elements;
6300     size_t                _size;
6301   };
6302
6303   //================================================================================
6304   /*!
6305    * \brief ElementBndBoxTree creation
6306    */
6307   //================================================================================
6308
6309   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6310     :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6311   {
6312     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6313     _elements.reserve( nbElems );
6314
6315     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6316     while ( elemIt->more() )
6317       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6318
6319     compute();
6320   }
6321
6322   //================================================================================
6323   /*!
6324    * \brief Destructor
6325    */
6326   //================================================================================
6327
6328   ElementBndBoxTree::~ElementBndBoxTree()
6329   {
6330     for ( int i = 0; i < _elements.size(); ++i )
6331       if ( --_elements[i]->_refCount <= 0 )
6332         delete _elements[i];
6333   }
6334
6335   //================================================================================
6336   /*!
6337    * \brief Return the maximal box
6338    */
6339   //================================================================================
6340
6341   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6342   {
6343     Bnd_B3d* box = new Bnd_B3d;
6344     for ( int i = 0; i < _elements.size(); ++i )
6345       box->Add( *_elements[i] );
6346     return box;
6347   }
6348
6349   //================================================================================
6350   /*!
6351    * \brief Redistrubute element boxes among children
6352    */
6353   //================================================================================
6354
6355   void ElementBndBoxTree::buildChildrenData()
6356   {
6357     for ( int i = 0; i < _elements.size(); ++i )
6358     {
6359       for (int j = 0; j < 8; j++)
6360       {
6361         if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6362         {
6363           _elements[i]->_refCount++;
6364           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6365         }
6366       }
6367       _elements[i]->_refCount--;
6368     }
6369     _size = _elements.size();
6370     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6371
6372     for (int j = 0; j < 8; j++)
6373     {
6374       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6375       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6376         child->myIsLeaf = true;
6377
6378       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6379         SMESHUtils::CompactVector( child->_elements );
6380     }
6381   }
6382
6383   //================================================================================
6384   /*!
6385    * \brief Return elements which can include the point
6386    */
6387   //================================================================================
6388
6389   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6390                                                 TIDSortedElemSet& foundElems)
6391   {
6392     if ( getBox()->IsOut( point.XYZ() ))
6393       return;
6394
6395     if ( isLeaf() )
6396     {
6397       for ( int i = 0; i < _elements.size(); ++i )
6398         if ( !_elements[i]->IsOut( point.XYZ() ))
6399           foundElems.insert( _elements[i]->_element );
6400     }
6401     else
6402     {
6403       for (int i = 0; i < 8; i++)
6404         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6405     }
6406   }
6407
6408   //================================================================================
6409   /*!
6410    * \brief Return elements which can be intersected by the line
6411    */
6412   //================================================================================
6413
6414   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6415                                                TIDSortedElemSet& foundElems)
6416   {
6417     if ( getBox()->IsOut( line ))
6418       return;
6419
6420     if ( isLeaf() )
6421     {
6422       for ( int i = 0; i < _elements.size(); ++i )
6423         if ( !_elements[i]->IsOut( line ))
6424           foundElems.insert( _elements[i]->_element );
6425     }
6426     else
6427     {
6428       for (int i = 0; i < 8; i++)
6429         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6430     }
6431   }
6432
6433   //================================================================================
6434   /*!
6435    * \brief Return elements from leaves intersecting the sphere
6436    */
6437   //================================================================================
6438
6439   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6440                                                 const double      radius,
6441                                                 TIDSortedElemSet& foundElems)
6442   {
6443     if ( getBox()->IsOut( center, radius ))
6444       return;
6445
6446     if ( isLeaf() )
6447     {
6448       for ( int i = 0; i < _elements.size(); ++i )
6449         if ( !_elements[i]->IsOut( center, radius ))
6450           foundElems.insert( _elements[i]->_element );
6451     }
6452     else
6453     {
6454       for (int i = 0; i < 8; i++)
6455         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6456     }
6457   }
6458
6459   //================================================================================
6460   /*!
6461    * \brief Construct the element box
6462    */
6463   //================================================================================
6464
6465   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6466   {
6467     _element  = elem;
6468     _refCount = 1;
6469     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6470     while ( nIt->more() )
6471       Add( SMESH_TNodeXYZ( nIt->next() ));
6472     Enlarge( tolerance );
6473   }
6474
6475 } // namespace
6476
6477 //=======================================================================
6478 /*!
6479  * \brief Implementation of search for the elements by point and
6480  *        of classification of point in 2D mesh
6481  */
6482 //=======================================================================
6483
6484 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6485 {
6486   SMESHDS_Mesh*                _mesh;
6487   SMDS_ElemIteratorPtr         _meshPartIt;
6488   ElementBndBoxTree*           _ebbTree;
6489   SMESH_NodeSearcherImpl*      _nodeSearcher;
6490   SMDSAbs_ElementType          _elementType;
6491   double                       _tolerance;
6492   bool                         _outerFacesFound;
6493   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6494
6495   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6496     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6497   ~SMESH_ElementSearcherImpl()
6498   {
6499     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6500     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6501   }
6502   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6503                                   SMDSAbs_ElementType                type,
6504                                   vector< const SMDS_MeshElement* >& foundElements);
6505   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6506   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6507                                                  SMDSAbs_ElementType type );
6508
6509   void GetElementsNearLine( const gp_Ax1&                      line,
6510                             SMDSAbs_ElementType                type,
6511                             vector< const SMDS_MeshElement* >& foundElems);
6512   double getTolerance();
6513   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6514                             const double tolerance, double & param);
6515   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6516   bool isOuterBoundary(const SMDS_MeshElement* face) const
6517   {
6518     return _outerFaces.empty() || _outerFaces.count(face);
6519   }
6520   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6521   {
6522     const SMDS_MeshElement* _face;
6523     gp_Vec                  _faceNorm;
6524     bool                    _coincides; //!< the line lays in face plane
6525     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6526       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6527   };
6528   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6529   {
6530     SMESH_TLink      _link;
6531     TIDSortedElemSet _faces;
6532     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6533       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6534   };
6535 };
6536
6537 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6538 {
6539   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6540              << ", _coincides="<<i._coincides << ")";
6541 }
6542
6543 //=======================================================================
6544 /*!
6545  * \brief define tolerance for search
6546  */
6547 //=======================================================================
6548
6549 double SMESH_ElementSearcherImpl::getTolerance()
6550 {
6551   if ( _tolerance < 0 )
6552   {
6553     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6554
6555     _tolerance = 0;
6556     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6557     {
6558       double boxSize = _nodeSearcher->getTree()->maxSize();
6559       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6560     }
6561     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6562     {
6563       double boxSize = _ebbTree->maxSize();
6564       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6565     }
6566     if ( _tolerance == 0 )
6567     {
6568       // define tolerance by size of a most complex element
6569       int complexType = SMDSAbs_Volume;
6570       while ( complexType > SMDSAbs_All &&
6571               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6572         --complexType;
6573       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6574       double elemSize;
6575       if ( complexType == int( SMDSAbs_Node ))
6576       {
6577         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6578         elemSize = 1;
6579         if ( meshInfo.NbNodes() > 2 )
6580           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6581       }
6582       else
6583       {
6584         SMDS_ElemIteratorPtr elemIt =
6585             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6586         const SMDS_MeshElement* elem = elemIt->next();
6587         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6588         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6589         elemSize = 0;
6590         while ( nodeIt->more() )
6591         {
6592           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6593           elemSize = max( dist, elemSize );
6594         }
6595       }
6596       _tolerance = 1e-4 * elemSize;
6597     }
6598   }
6599   return _tolerance;
6600 }
6601
6602 //================================================================================
6603 /*!
6604  * \brief Find intersection of the line and an edge of face and return parameter on line
6605  */
6606 //================================================================================
6607
6608 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6609                                                      const SMDS_MeshElement* face,
6610                                                      const double            tol,
6611                                                      double &                param)
6612 {
6613   int nbInts = 0;
6614   param = 0;
6615
6616   GeomAPI_ExtremaCurveCurve anExtCC;
6617   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6618
6619   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6620   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6621   {
6622     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6623                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6624     anExtCC.Init( lineCurve, edge);
6625     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6626     {
6627       Quantity_Parameter pl, pe;
6628       anExtCC.LowerDistanceParameters( pl, pe );
6629       param += pl;
6630       if ( ++nbInts == 2 )
6631         break;
6632     }
6633   }
6634   if ( nbInts > 0 ) param /= nbInts;
6635   return nbInts > 0;
6636 }
6637 //================================================================================
6638 /*!
6639  * \brief Find all faces belonging to the outer boundary of mesh
6640  */
6641 //================================================================================
6642
6643 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6644 {
6645   if ( _outerFacesFound ) return;
6646
6647   // Collect all outer faces by passing from one outer face to another via their links
6648   // and BTW find out if there are internal faces at all.
6649
6650   // checked links and links where outer boundary meets internal one
6651   set< SMESH_TLink > visitedLinks, seamLinks;
6652
6653   // links to treat with already visited faces sharing them
6654   list < TFaceLink > startLinks;
6655
6656   // load startLinks with the first outerFace
6657   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6658   _outerFaces.insert( outerFace );
6659
6660   TIDSortedElemSet emptySet;
6661   while ( !startLinks.empty() )
6662   {
6663     const SMESH_TLink& link  = startLinks.front()._link;
6664     TIDSortedElemSet&  faces = startLinks.front()._faces;
6665
6666     outerFace = *faces.begin();
6667     // find other faces sharing the link
6668     const SMDS_MeshElement* f;
6669     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6670       faces.insert( f );
6671
6672     // select another outer face among the found
6673     const SMDS_MeshElement* outerFace2 = 0;
6674     if ( faces.size() == 2 )
6675     {
6676       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6677     }
6678     else if ( faces.size() > 2 )
6679     {
6680       seamLinks.insert( link );
6681
6682       // link direction within the outerFace
6683       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6684                    SMESH_TNodeXYZ( link.node2()));
6685       int i1 = outerFace->GetNodeIndex( link.node1() );
6686       int i2 = outerFace->GetNodeIndex( link.node2() );
6687       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6688       if ( rev ) n1n2.Reverse();
6689       // outerFace normal
6690       gp_XYZ ofNorm, fNorm;
6691       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6692       {
6693         // direction from the link inside outerFace
6694         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6695         // sort all other faces by angle with the dirInOF
6696         map< double, const SMDS_MeshElement* > angle2Face;
6697         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6698         for ( ; face != faces.end(); ++face )
6699         {
6700           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6701             continue;
6702           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6703           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6704           if ( angle < 0 ) angle += 2. * M_PI;
6705           angle2Face.insert( make_pair( angle, *face ));
6706         }
6707         if ( !angle2Face.empty() )
6708           outerFace2 = angle2Face.begin()->second;
6709       }
6710     }
6711     // store the found outer face and add its links to continue seaching from
6712     if ( outerFace2 )
6713     {
6714       _outerFaces.insert( outerFace );
6715       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6716       for ( int i = 0; i < nbNodes; ++i )
6717       {
6718         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6719         if ( visitedLinks.insert( link2 ).second )
6720           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6721       }
6722     }
6723     startLinks.pop_front();
6724   }
6725   _outerFacesFound = true;
6726
6727   if ( !seamLinks.empty() )
6728   {
6729     // There are internal boundaries touching the outher one,
6730     // find all faces of internal boundaries in order to find
6731     // faces of boundaries of holes, if any.
6732
6733   }
6734   else
6735   {
6736     _outerFaces.clear();
6737   }
6738 }
6739
6740 //=======================================================================
6741 /*!
6742  * \brief Find elements of given type where the given point is IN or ON.
6743  *        Returns nb of found elements and elements them-selves.
6744  *
6745  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6746  */
6747 //=======================================================================
6748
6749 int SMESH_ElementSearcherImpl::
6750 FindElementsByPoint(const gp_Pnt&                      point,
6751                     SMDSAbs_ElementType                type,
6752                     vector< const SMDS_MeshElement* >& foundElements)
6753 {
6754   foundElements.clear();
6755
6756   double tolerance = getTolerance();
6757
6758   // =================================================================================
6759   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6760   {
6761     if ( !_nodeSearcher )
6762       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6763
6764     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6765     if ( !closeNode ) return foundElements.size();
6766
6767     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6768       return foundElements.size(); // to far from any node
6769
6770     if ( type == SMDSAbs_Node )
6771     {
6772       foundElements.push_back( closeNode );
6773     }
6774     else
6775     {
6776       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6777       while ( elemIt->more() )
6778         foundElements.push_back( elemIt->next() );
6779     }
6780   }
6781   // =================================================================================
6782   else // elements more complex than 0D
6783   {
6784     if ( !_ebbTree || _elementType != type )
6785     {
6786       if ( _ebbTree ) delete _ebbTree;
6787       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6788     }
6789     TIDSortedElemSet suspectElems;
6790     _ebbTree->getElementsNearPoint( point, suspectElems );
6791     TIDSortedElemSet::iterator elem = suspectElems.begin();
6792     for ( ; elem != suspectElems.end(); ++elem )
6793       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6794         foundElements.push_back( *elem );
6795   }
6796   return foundElements.size();
6797 }
6798
6799 //=======================================================================
6800 /*!
6801  * \brief Find an element of given type most close to the given point
6802  *
6803  * WARNING: Only face search is implemeneted so far
6804  */
6805 //=======================================================================
6806
6807 const SMDS_MeshElement*
6808 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6809                                           SMDSAbs_ElementType type )
6810 {
6811   const SMDS_MeshElement* closestElem = 0;
6812
6813   if ( type == SMDSAbs_Face )
6814   {
6815     if ( !_ebbTree || _elementType != type )
6816     {
6817       if ( _ebbTree ) delete _ebbTree;
6818       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6819     }
6820     TIDSortedElemSet suspectElems;
6821     _ebbTree->getElementsNearPoint( point, suspectElems );
6822
6823     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6824     {
6825       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6826                                  _ebbTree->getBox()->CornerMax() );
6827       double radius;
6828       if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6829         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6830       else
6831         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6832       while ( suspectElems.empty() )
6833       {
6834         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6835         radius *= 1.1;
6836       }
6837     }
6838     double minDist = std::numeric_limits<double>::max();
6839     multimap< double, const SMDS_MeshElement* > dist2face;
6840     TIDSortedElemSet::iterator elem = suspectElems.begin();
6841     for ( ; elem != suspectElems.end(); ++elem )
6842     {
6843       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6844                                                    point );
6845       if ( dist < minDist + 1e-10)
6846       {
6847         minDist = dist;
6848         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6849       }
6850     }
6851     if ( !dist2face.empty() )
6852     {
6853       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6854       closestElem = d2f->second;
6855       // if there are several elements at the same distance, select one
6856       // with GC closest to the point
6857       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6858       double minDistToGC = 0;
6859       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6860       {
6861         if ( minDistToGC == 0 )
6862         {
6863           gp_XYZ gc(0,0,0);
6864           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6865                            TXyzIterator(), gc ) / closestElem->NbNodes();
6866           minDistToGC = point.SquareDistance( gc );
6867         }
6868         gp_XYZ gc(0,0,0);
6869         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6870                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6871         double d = point.SquareDistance( gc );
6872         if ( d < minDistToGC )
6873         {
6874           minDistToGC = d;
6875           closestElem = d2f->second;
6876         }
6877       }
6878       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6879       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6880     }
6881   }
6882   else
6883   {
6884     // NOT IMPLEMENTED SO FAR
6885   }
6886   return closestElem;
6887 }
6888
6889
6890 //================================================================================
6891 /*!
6892  * \brief Classify the given point in the closed 2D mesh
6893  */
6894 //================================================================================
6895
6896 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6897 {
6898   double tolerance = getTolerance();
6899   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6900   {
6901     if ( _ebbTree ) delete _ebbTree;
6902     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6903   }
6904   // Algo: analyse transition of a line starting at the point through mesh boundary;
6905   // try three lines parallel to axis of the coordinate system and perform rough
6906   // analysis. If solution is not clear perform thorough analysis.
6907
6908   const int nbAxes = 3;
6909   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6910   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6911   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6912   multimap< int, int > nbInt2Axis; // to find the simplest case
6913   for ( int axis = 0; axis < nbAxes; ++axis )
6914   {
6915     gp_Ax1 lineAxis( point, axisDir[axis]);
6916     gp_Lin line    ( lineAxis );
6917
6918     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6919     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6920
6921     // Intersect faces with the line
6922
6923     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6924     TIDSortedElemSet::iterator face = suspectFaces.begin();
6925     for ( ; face != suspectFaces.end(); ++face )
6926     {
6927       // get face plane
6928       gp_XYZ fNorm;
6929       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6930       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6931
6932       // perform intersection
6933       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6934       if ( !intersection.IsDone() )
6935         continue;
6936       if ( intersection.IsInQuadric() )
6937       {
6938         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6939       }
6940       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6941       {
6942         gp_Pnt intersectionPoint = intersection.Point(1);
6943         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6944           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6945       }
6946     }
6947     // Analyse intersections roughly
6948
6949     int nbInter = u2inters.size();
6950     if ( nbInter == 0 )
6951       return TopAbs_OUT;
6952
6953     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6954     if ( nbInter == 1 ) // not closed mesh
6955       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6956
6957     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6958       return TopAbs_ON;
6959
6960     if ( (f<0) == (l<0) )
6961       return TopAbs_OUT;
6962
6963     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6964     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
6965     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6966       return TopAbs_IN;
6967
6968     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6969
6970     if ( _outerFacesFound ) break; // pass to thorough analysis
6971
6972   } // three attempts - loop on CS axes
6973
6974   // Analyse intersections thoroughly.
6975   // We make two loops maximum, on the first one we only exclude touching intersections,
6976   // on the second, if situation is still unclear, we gather and use information on
6977   // position of faces (internal or outer). If faces position is already gathered,
6978   // we make the second loop right away.
6979
6980   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6981   {
6982     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6983     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6984     {
6985       int axis = nb_axis->second;
6986       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6987
6988       gp_Ax1 lineAxis( point, axisDir[axis]);
6989       gp_Lin line    ( lineAxis );
6990
6991       // add tangent intersections to u2inters
6992       double param;
6993       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6994       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6995         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6996           u2inters.insert(make_pair( param, *tgtInt ));
6997       tangentInters[ axis ].clear();
6998
6999       // Count intersections before and after the point excluding touching ones.
7000       // If hasPositionInfo we count intersections of outer boundary only
7001
7002       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7003       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7004       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7005       bool ok = ! u_int1->second._coincides;
7006       while ( ok && u_int1 != u2inters.end() )
7007       {
7008         double u = u_int1->first;
7009         bool touchingInt = false;
7010         if ( ++u_int2 != u2inters.end() )
7011         {
7012           // skip intersections at the same point (if the line passes through edge or node)
7013           int nbSamePnt = 0;
7014           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7015           {
7016             ++nbSamePnt;
7017             ++u_int2;
7018           }
7019
7020           // skip tangent intersections
7021           int nbTgt = 0;
7022           const SMDS_MeshElement* prevFace = u_int1->second._face;
7023           while ( ok && u_int2->second._coincides )
7024           {
7025             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7026               ok = false;
7027             else
7028             {
7029               nbTgt++;
7030               u_int2++;
7031               ok = ( u_int2 != u2inters.end() );
7032             }
7033           }
7034           if ( !ok ) break;
7035
7036           // skip intersections at the same point after tangent intersections
7037           if ( nbTgt > 0 )
7038           {
7039             double u2 = u_int2->first;
7040             ++u_int2;
7041             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7042             {
7043               ++nbSamePnt;
7044               ++u_int2;
7045             }
7046           }
7047           // decide if we skipped a touching intersection
7048           if ( nbSamePnt + nbTgt > 0 )
7049           {
7050             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7051             map< double, TInters >::iterator u_int = u_int1;
7052             for ( ; u_int != u_int2; ++u_int )
7053             {
7054               if ( u_int->second._coincides ) continue;
7055               double dot = u_int->second._faceNorm * line.Direction();
7056               if ( dot > maxDot ) maxDot = dot;
7057               if ( dot < minDot ) minDot = dot;
7058             }
7059             touchingInt = ( minDot*maxDot < 0 );
7060           }
7061         }
7062         if ( !touchingInt )
7063         {
7064           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7065           {
7066             if ( u < 0 )
7067               ++nbIntBeforePoint;
7068             else
7069               ++nbIntAfterPoint;
7070           }
7071           if ( u < f ) f = u;
7072           if ( u > l ) l = u;
7073         }
7074
7075         u_int1 = u_int2; // to next intersection
7076
7077       } // loop on intersections with one line
7078
7079       if ( ok )
7080       {
7081         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7082           return TopAbs_ON;
7083
7084         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
7085           return TopAbs_OUT;
7086
7087         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7088           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7089
7090         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7091           return TopAbs_IN;
7092
7093         if ( (f<0) == (l<0) )
7094           return TopAbs_OUT;
7095
7096         if ( hasPositionInfo )
7097           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7098       }
7099     } // loop on intersections of the tree lines - thorough analysis
7100
7101     if ( !hasPositionInfo )
7102     {
7103       // gather info on faces position - is face in the outer boundary or not
7104       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7105       findOuterBoundary( u2inters.begin()->second._face );
7106     }
7107
7108   } // two attempts - with and w/o faces position info in the mesh
7109
7110   return TopAbs_UNKNOWN;
7111 }
7112
7113 //=======================================================================
7114 /*!
7115  * \brief Return elements possibly intersecting the line
7116  */
7117 //=======================================================================
7118
7119 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7120                                                      SMDSAbs_ElementType                type,
7121                                                      vector< const SMDS_MeshElement* >& foundElems)
7122 {
7123   if ( !_ebbTree || _elementType != type )
7124   {
7125     if ( _ebbTree ) delete _ebbTree;
7126     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7127   }
7128   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7129   _ebbTree->getElementsNearLine( line, suspectFaces );
7130   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7131 }
7132
7133 //=======================================================================
7134 /*!
7135  * \brief Return SMESH_ElementSearcher
7136  */
7137 //=======================================================================
7138
7139 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7140 {
7141   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7142 }
7143
7144 //=======================================================================
7145 /*!
7146  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7147  */
7148 //=======================================================================
7149
7150 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7151 {
7152   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7153 }
7154
7155 //=======================================================================
7156 /*!
7157  * \brief Return true if the point is IN or ON of the element
7158  */
7159 //=======================================================================
7160
7161 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7162 {
7163   if ( element->GetType() == SMDSAbs_Volume)
7164   {
7165     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7166   }
7167
7168   // get ordered nodes
7169
7170   vector< gp_XYZ > xyz;
7171   vector<const SMDS_MeshNode*> nodeList;
7172
7173   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7174   if ( element->IsQuadratic() ) {
7175     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7176       nodeIt = f->interlacedNodesElemIterator();
7177     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7178       nodeIt = e->interlacedNodesElemIterator();
7179   }
7180   while ( nodeIt->more() )
7181     {
7182       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7183       xyz.push_back( SMESH_TNodeXYZ(node) );
7184       nodeList.push_back(node);
7185     }
7186
7187   int i, nbNodes = element->NbNodes();
7188
7189   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7190   {
7191     // compute face normal
7192     gp_Vec faceNorm(0,0,0);
7193     xyz.push_back( xyz.front() );
7194     nodeList.push_back( nodeList.front() );
7195     for ( i = 0; i < nbNodes; ++i )
7196     {
7197       gp_Vec edge1( xyz[i+1], xyz[i]);
7198       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7199       faceNorm += edge1 ^ edge2;
7200     }
7201     double normSize = faceNorm.Magnitude();
7202     if ( normSize <= tol )
7203     {
7204       // degenerated face: point is out if it is out of all face edges
7205       for ( i = 0; i < nbNodes; ++i )
7206       {
7207         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7208         if ( !IsOut( &edge, point, tol ))
7209           return false;
7210       }
7211       return true;
7212     }
7213     faceNorm /= normSize;
7214
7215     // check if the point lays on face plane
7216     gp_Vec n2p( xyz[0], point );
7217     if ( fabs( n2p * faceNorm ) > tol )
7218       return true; // not on face plane
7219
7220     // check if point is out of face boundary:
7221     // define it by closest transition of a ray point->infinity through face boundary
7222     // on the face plane.
7223     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7224     // to find intersections of the ray with the boundary.
7225     gp_Vec ray = n2p;
7226     gp_Vec plnNorm = ray ^ faceNorm;
7227     normSize = plnNorm.Magnitude();
7228     if ( normSize <= tol ) return false; // point coincides with the first node
7229     plnNorm /= normSize;
7230     // for each node of the face, compute its signed distance to the plane
7231     vector<double> dist( nbNodes + 1);
7232     for ( i = 0; i < nbNodes; ++i )
7233     {
7234       gp_Vec n2p( xyz[i], point );
7235       dist[i] = n2p * plnNorm;
7236     }
7237     dist.back() = dist.front();
7238     // find the closest intersection
7239     int    iClosest = -1;
7240     double rClosest, distClosest = 1e100;;
7241     gp_Pnt pClosest;
7242     for ( i = 0; i < nbNodes; ++i )
7243     {
7244       double r;
7245       if ( fabs( dist[i]) < tol )
7246         r = 0.;
7247       else if ( fabs( dist[i+1]) < tol )
7248         r = 1.;
7249       else if ( dist[i] * dist[i+1] < 0 )
7250         r = dist[i] / ( dist[i] - dist[i+1] );
7251       else
7252         continue; // no intersection
7253       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7254       gp_Vec p2int ( point, pInt);
7255       if ( p2int * ray > -tol ) // right half-space
7256       {
7257         double intDist = p2int.SquareMagnitude();
7258         if ( intDist < distClosest )
7259         {
7260           iClosest = i;
7261           rClosest = r;
7262           pClosest = pInt;
7263           distClosest = intDist;
7264         }
7265       }
7266     }
7267     if ( iClosest < 0 )
7268       return true; // no intesections - out
7269
7270     // analyse transition
7271     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7272     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7273     gp_Vec p2int ( point, pClosest );
7274     bool out = (edgeNorm * p2int) < -tol;
7275     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7276       return out;
7277
7278     // ray pass through a face node; analyze transition through an adjacent edge
7279     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7280     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7281     gp_Vec edgeAdjacent( p1, p2 );
7282     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7283     bool out2 = (edgeNorm2 * p2int) < -tol;
7284
7285     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7286     return covexCorner ? (out || out2) : (out && out2);
7287   }
7288   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7289   {
7290     // point is out of edge if it is NOT ON any straight part of edge
7291     // (we consider quadratic edge as being composed of two straight parts)
7292     for ( i = 1; i < nbNodes; ++i )
7293     {
7294       gp_Vec edge( xyz[i-1], xyz[i]);
7295       gp_Vec n1p ( xyz[i-1], point);
7296       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7297       if ( dist > tol )
7298         continue;
7299       gp_Vec n2p( xyz[i], point );
7300       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7301         continue;
7302       return false; // point is ON this part
7303     }
7304     return true;
7305   }
7306   // Node or 0D element -------------------------------------------------------------------------
7307   {
7308     gp_Vec n2p ( xyz[0], point );
7309     return n2p.Magnitude() <= tol;
7310   }
7311   return true;
7312 }
7313
7314 //=======================================================================
7315
7316 namespace
7317 {
7318   // Position of a point relative to a segment
7319   //            .           .
7320   //            .  LEFT     .
7321   //            .           .
7322   //  VERTEX 1  o----ON----->  VERTEX 2
7323   //            .           .
7324   //            .  RIGHT    .
7325   //            .           .
7326   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7327                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7328   struct PointPos
7329   {
7330     PositionName _name;
7331     int          _index; // index of vertex or segment
7332
7333     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7334     bool operator < (const PointPos& other ) const
7335     {
7336       if ( _name == other._name )
7337         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7338       return _name < other._name;
7339     }
7340   };
7341
7342   //================================================================================
7343   /*!
7344    * \brief Return of a point relative to a segment
7345    *  \param point2D      - the point to analyze position of
7346    *  \param xyVec        - end points of segments
7347    *  \param index0       - 0-based index of the first point of segment
7348    *  \param posToFindOut - flags of positions to detect
7349    *  \retval PointPos - point position
7350    */
7351   //================================================================================
7352
7353   PointPos getPointPosition( const gp_XY& point2D,
7354                              const gp_XY* segEnds,
7355                              const int    index0 = 0,
7356                              const int    posToFindOut = POS_ALL)
7357   {
7358     const gp_XY& p1 = segEnds[ index0   ];
7359     const gp_XY& p2 = segEnds[ index0+1 ];
7360     const gp_XY grad = p2 - p1;
7361
7362     if ( posToFindOut & POS_VERTEX )
7363     {
7364       // check if the point2D is at "vertex 1" zone
7365       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7366                                   p1.Y() + grad.X() ) };
7367       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7368         return PointPos( POS_VERTEX, index0 );
7369
7370       // check if the point2D is at "vertex 2" zone
7371       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7372                                   p2.Y() + grad.X() ) };
7373       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7374         return PointPos( POS_VERTEX, index0 + 1);
7375     }
7376     double edgeEquation =
7377       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7378     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7379   }
7380 }
7381
7382 //=======================================================================
7383 /*!
7384  * \brief Return minimal distance from a point to a face
7385  *
7386  * Currently we ignore non-planarity and 2nd order of face
7387  */
7388 //=======================================================================
7389
7390 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7391                                       const gp_Pnt&        point )
7392 {
7393   double badDistance = -1;
7394   if ( !face ) return badDistance;
7395
7396   // coordinates of nodes (medium nodes, if any, ignored)
7397   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7398   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7399   xyz.resize( face->NbCornerNodes()+1 );
7400
7401   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7402   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7403   gp_Trsf trsf;
7404   gp_Vec OZ ( xyz[0], xyz[1] );
7405   gp_Vec OX ( xyz[0], xyz[2] );
7406   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7407   {
7408     if ( xyz.size() < 4 ) return badDistance;
7409     OZ = gp_Vec ( xyz[0], xyz[2] );
7410     OX = gp_Vec ( xyz[0], xyz[3] );
7411   }
7412   gp_Ax3 tgtCS;
7413   try {
7414     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7415   }
7416   catch ( Standard_Failure ) {
7417     return badDistance;
7418   }
7419   trsf.SetTransformation( tgtCS );
7420
7421   // move all the nodes to 2D
7422   vector<gp_XY> xy( xyz.size() );
7423   for ( size_t i = 0;i < xyz.size()-1; ++i )
7424   {
7425     gp_XYZ p3d = xyz[i];
7426     trsf.Transforms( p3d );
7427     xy[i].SetCoord( p3d.X(), p3d.Z() );
7428   }
7429   xyz.back() = xyz.front();
7430   xy.back() = xy.front();
7431
7432   // // move the point in 2D
7433   gp_XYZ tmpPnt = point.XYZ();
7434   trsf.Transforms( tmpPnt );
7435   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7436
7437   // loop on segments of the face to analyze point position ralative to the face
7438   set< PointPos > pntPosSet;
7439   for ( size_t i = 1; i < xy.size(); ++i )
7440   {
7441     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7442     pntPosSet.insert( pos );
7443   }
7444
7445   // compute distance
7446   PointPos pos = *pntPosSet.begin();
7447   // cout << "Face " << face->GetID() << " DIST: ";
7448   switch ( pos._name )
7449   {
7450   case POS_LEFT: {
7451     // point is most close to a segment
7452     gp_Vec p0p1( point, xyz[ pos._index ] );
7453     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7454     p1p2.Normalize();
7455     double projDist = p0p1 * p1p2; // distance projected to the segment
7456     gp_Vec projVec = p1p2 * projDist;
7457     gp_Vec distVec = p0p1 - projVec;
7458     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7459     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7460     return distVec.Magnitude();
7461   }
7462   case POS_RIGHT: {
7463     // point is inside the face
7464     double distToFacePlane = tmpPnt.Y();
7465     // cout << distToFacePlane << ", INSIDE " << endl;
7466     return Abs( distToFacePlane );
7467   }
7468   case POS_VERTEX: {
7469     // point is most close to a node
7470     gp_Vec distVec( point, xyz[ pos._index ]);
7471     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7472     return distVec.Magnitude();
7473   }
7474   }
7475   return badDistance;
7476 }
7477
7478 //=======================================================================
7479 //function : SimplifyFace
7480 //purpose  :
7481 //=======================================================================
7482 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7483                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7484                                     vector<int>&                        quantities) const
7485 {
7486   int nbNodes = faceNodes.size();
7487
7488   if (nbNodes < 3)
7489     return 0;
7490
7491   set<const SMDS_MeshNode*> nodeSet;
7492
7493   // get simple seq of nodes
7494   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7495   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7496   int iSimple = 0, nbUnique = 0;
7497
7498   simpleNodes[iSimple++] = faceNodes[0];
7499   nbUnique++;
7500   for (int iCur = 1; iCur < nbNodes; iCur++) {
7501     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7502       simpleNodes[iSimple++] = faceNodes[iCur];
7503       if (nodeSet.insert( faceNodes[iCur] ).second)
7504         nbUnique++;
7505     }
7506   }
7507   int nbSimple = iSimple;
7508   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7509     nbSimple--;
7510     iSimple--;
7511   }
7512
7513   if (nbUnique < 3)
7514     return 0;
7515
7516   // separate loops
7517   int nbNew = 0;
7518   bool foundLoop = (nbSimple > nbUnique);
7519   while (foundLoop) {
7520     foundLoop = false;
7521     set<const SMDS_MeshNode*> loopSet;
7522     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7523       const SMDS_MeshNode* n = simpleNodes[iSimple];
7524       if (!loopSet.insert( n ).second) {
7525         foundLoop = true;
7526
7527         // separate loop
7528         int iC = 0, curLast = iSimple;
7529         for (; iC < curLast; iC++) {
7530           if (simpleNodes[iC] == n) break;
7531         }
7532         int loopLen = curLast - iC;
7533         if (loopLen > 2) {
7534           // create sub-element
7535           nbNew++;
7536           quantities.push_back(loopLen);
7537           for (; iC < curLast; iC++) {
7538             poly_nodes.push_back(simpleNodes[iC]);
7539           }
7540         }
7541         // shift the rest nodes (place from the first loop position)
7542         for (iC = curLast + 1; iC < nbSimple; iC++) {
7543           simpleNodes[iC - loopLen] = simpleNodes[iC];
7544         }
7545         nbSimple -= loopLen;
7546         iSimple -= loopLen;
7547       }
7548     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7549   } // while (foundLoop)
7550
7551   if (iSimple > 2) {
7552     nbNew++;
7553     quantities.push_back(iSimple);
7554     for (int i = 0; i < iSimple; i++)
7555       poly_nodes.push_back(simpleNodes[i]);
7556   }
7557
7558   return nbNew;
7559 }
7560
7561 //=======================================================================
7562 //function : MergeNodes
7563 //purpose  : In each group, the cdr of nodes are substituted by the first one
7564 //           in all elements.
7565 //=======================================================================
7566
7567 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7568 {
7569   MESSAGE("MergeNodes");
7570   myLastCreatedElems.Clear();
7571   myLastCreatedNodes.Clear();
7572
7573   SMESHDS_Mesh* aMesh = GetMeshDS();
7574
7575   TNodeNodeMap nodeNodeMap; // node to replace - new node
7576   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7577   list< int > rmElemIds, rmNodeIds;
7578
7579   // Fill nodeNodeMap and elems
7580
7581   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7582   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7583     list<const SMDS_MeshNode*>& nodes = *grIt;
7584     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7585     const SMDS_MeshNode* nToKeep = *nIt;
7586     //MESSAGE("node to keep " << nToKeep->GetID());
7587     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7588       const SMDS_MeshNode* nToRemove = *nIt;
7589       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7590       if ( nToRemove != nToKeep ) {
7591         //MESSAGE("  node to remove " << nToRemove->GetID());
7592         rmNodeIds.push_back( nToRemove->GetID() );
7593         AddToSameGroups( nToKeep, nToRemove, aMesh );
7594       }
7595
7596       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7597       while ( invElemIt->more() ) {
7598         const SMDS_MeshElement* elem = invElemIt->next();
7599         elems.insert(elem);
7600       }
7601     }
7602   }
7603   // Change element nodes or remove an element
7604
7605   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7606   for ( ; eIt != elems.end(); eIt++ ) {
7607     const SMDS_MeshElement* elem = *eIt;
7608     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7609     int nbNodes = elem->NbNodes();
7610     int aShapeId = FindShape( elem );
7611
7612     set<const SMDS_MeshNode*> nodeSet;
7613     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7614     int iUnique = 0, iCur = 0, nbRepl = 0;
7615     vector<int> iRepl( nbNodes );
7616
7617     // get new seq of nodes
7618     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7619     while ( itN->more() ) {
7620       const SMDS_MeshNode* n =
7621         static_cast<const SMDS_MeshNode*>( itN->next() );
7622
7623       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7624       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7625         n = (*nnIt).second;
7626         // BUG 0020185: begin
7627         {
7628           bool stopRecur = false;
7629           set<const SMDS_MeshNode*> nodesRecur;
7630           nodesRecur.insert(n);
7631           while (!stopRecur) {
7632             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7633             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7634               n = (*nnIt_i).second;
7635               if (!nodesRecur.insert(n).second) {
7636                 // error: recursive dependancy
7637                 stopRecur = true;
7638               }
7639             }
7640             else
7641               stopRecur = true;
7642           }
7643         }
7644         // BUG 0020185: end
7645       }
7646       curNodes[ iCur ] = n;
7647       bool isUnique = nodeSet.insert( n ).second;
7648       if ( isUnique )
7649         uniqueNodes[ iUnique++ ] = n;
7650       else
7651         iRepl[ nbRepl++ ] = iCur;
7652       iCur++;
7653     }
7654
7655     // Analyse element topology after replacement
7656
7657     bool isOk = true;
7658     int nbUniqueNodes = nodeSet.size();
7659     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7660     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7661       // Polygons and Polyhedral volumes
7662       if (elem->IsPoly()) {
7663
7664         if (elem->GetType() == SMDSAbs_Face) {
7665           // Polygon
7666           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7667           int inode = 0;
7668           for (; inode < nbNodes; inode++) {
7669             face_nodes[inode] = curNodes[inode];
7670           }
7671
7672           vector<const SMDS_MeshNode *> polygons_nodes;
7673           vector<int> quantities;
7674           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7675           if (nbNew > 0) {
7676             inode = 0;
7677             for (int iface = 0; iface < nbNew; iface++) {
7678               int nbNodes = quantities[iface];
7679               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7680               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7681                 poly_nodes[ii] = polygons_nodes[inode];
7682               }
7683               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7684               myLastCreatedElems.Append(newElem);
7685               if (aShapeId)
7686                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7687             }
7688
7689             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7690             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7691             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7692             int quid =0;
7693             if (nbNew > 0) quid = nbNew - 1;
7694             vector<int> newquant(quantities.begin()+quid, quantities.end());
7695             const SMDS_MeshElement* newElem = 0;
7696             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7697             myLastCreatedElems.Append(newElem);
7698             if ( aShapeId && newElem )
7699               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7700             rmElemIds.push_back(elem->GetID());
7701           }
7702           else {
7703             rmElemIds.push_back(elem->GetID());
7704           }
7705
7706         }
7707         else if (elem->GetType() == SMDSAbs_Volume) {
7708           // Polyhedral volume
7709           if (nbUniqueNodes < 4) {
7710             rmElemIds.push_back(elem->GetID());
7711           }
7712           else {
7713             // each face has to be analyzed in order to check volume validity
7714             const SMDS_VtkVolume* aPolyedre =
7715               dynamic_cast<const SMDS_VtkVolume*>( elem );
7716             if (aPolyedre) {
7717               int nbFaces = aPolyedre->NbFaces();
7718
7719               vector<const SMDS_MeshNode *> poly_nodes;
7720               vector<int> quantities;
7721
7722               for (int iface = 1; iface <= nbFaces; iface++) {
7723                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7724                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7725
7726                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7727                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7728                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7729                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7730                     faceNode = (*nnIt).second;
7731                   }
7732                   faceNodes[inode - 1] = faceNode;
7733                 }
7734
7735                 SimplifyFace(faceNodes, poly_nodes, quantities);
7736               }
7737
7738               if (quantities.size() > 3) {
7739                 // to be done: remove coincident faces
7740               }
7741
7742               if (quantities.size() > 3)
7743                 {
7744                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7745                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7746                   const SMDS_MeshElement* newElem = 0;
7747                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7748                   myLastCreatedElems.Append(newElem);
7749                   if ( aShapeId && newElem )
7750                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7751                   rmElemIds.push_back(elem->GetID());
7752                 }
7753             }
7754             else {
7755               rmElemIds.push_back(elem->GetID());
7756             }
7757           }
7758         }
7759         else {
7760         }
7761
7762         continue;
7763       } // poly element
7764
7765       // Regular elements
7766       // TODO not all the possible cases are solved. Find something more generic?
7767       switch ( nbNodes ) {
7768       case 2: ///////////////////////////////////// EDGE
7769         isOk = false; break;
7770       case 3: ///////////////////////////////////// TRIANGLE
7771         isOk = false; break;
7772       case 4:
7773         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7774           isOk = false;
7775         else { //////////////////////////////////// QUADRANGLE
7776           if ( nbUniqueNodes < 3 )
7777             isOk = false;
7778           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7779             isOk = false; // opposite nodes stick
7780           //MESSAGE("isOk " << isOk);
7781         }
7782         break;
7783       case 6: ///////////////////////////////////// PENTAHEDRON
7784         if ( nbUniqueNodes == 4 ) {
7785           // ---------------------------------> tetrahedron
7786           if (nbRepl == 3 &&
7787               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7788             // all top nodes stick: reverse a bottom
7789             uniqueNodes[ 0 ] = curNodes [ 1 ];
7790             uniqueNodes[ 1 ] = curNodes [ 0 ];
7791           }
7792           else if (nbRepl == 3 &&
7793                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7794             // all bottom nodes stick: set a top before
7795             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7796             uniqueNodes[ 0 ] = curNodes [ 3 ];
7797             uniqueNodes[ 1 ] = curNodes [ 4 ];
7798             uniqueNodes[ 2 ] = curNodes [ 5 ];
7799           }
7800           else if (nbRepl == 4 &&
7801                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7802             // a lateral face turns into a line: reverse a bottom
7803             uniqueNodes[ 0 ] = curNodes [ 1 ];
7804             uniqueNodes[ 1 ] = curNodes [ 0 ];
7805           }
7806           else
7807             isOk = false;
7808         }
7809         else if ( nbUniqueNodes == 5 ) {
7810           // PENTAHEDRON --------------------> 2 tetrahedrons
7811           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7812             // a bottom node sticks with a linked top one
7813             // 1.
7814             SMDS_MeshElement* newElem =
7815               aMesh->AddVolume(curNodes[ 3 ],
7816                                curNodes[ 4 ],
7817                                curNodes[ 5 ],
7818                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7819             myLastCreatedElems.Append(newElem);
7820             if ( aShapeId )
7821               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7822             // 2. : reverse a bottom
7823             uniqueNodes[ 0 ] = curNodes [ 1 ];
7824             uniqueNodes[ 1 ] = curNodes [ 0 ];
7825             nbUniqueNodes = 4;
7826           }
7827           else
7828             isOk = false;
7829         }
7830         else
7831           isOk = false;
7832         break;
7833       case 8: {
7834         if(elem->IsQuadratic()) { // Quadratic quadrangle
7835           //   1    5    2
7836           //    +---+---+
7837           //    |       |
7838           //    |       |
7839           //   4+       +6
7840           //    |       |
7841           //    |       |
7842           //    +---+---+
7843           //   0    7    3
7844           isOk = false;
7845           if(nbRepl==2) {
7846             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7847           }
7848           if(nbRepl==3) {
7849             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7850             nbUniqueNodes = 6;
7851             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7852               uniqueNodes[0] = curNodes[0];
7853               uniqueNodes[1] = curNodes[2];
7854               uniqueNodes[2] = curNodes[3];
7855               uniqueNodes[3] = curNodes[5];
7856               uniqueNodes[4] = curNodes[6];
7857               uniqueNodes[5] = curNodes[7];
7858               isOk = true;
7859             }
7860             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7861               uniqueNodes[0] = curNodes[0];
7862               uniqueNodes[1] = curNodes[1];
7863               uniqueNodes[2] = curNodes[2];
7864               uniqueNodes[3] = curNodes[4];
7865               uniqueNodes[4] = curNodes[5];
7866               uniqueNodes[5] = curNodes[6];
7867               isOk = true;
7868             }
7869             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7870               uniqueNodes[0] = curNodes[1];
7871               uniqueNodes[1] = curNodes[2];
7872               uniqueNodes[2] = curNodes[3];
7873               uniqueNodes[3] = curNodes[5];
7874               uniqueNodes[4] = curNodes[6];
7875               uniqueNodes[5] = curNodes[0];
7876               isOk = true;
7877             }
7878             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7879               uniqueNodes[0] = curNodes[0];
7880               uniqueNodes[1] = curNodes[1];
7881               uniqueNodes[2] = curNodes[3];
7882               uniqueNodes[3] = curNodes[4];
7883               uniqueNodes[4] = curNodes[6];
7884               uniqueNodes[5] = curNodes[7];
7885               isOk = true;
7886             }
7887             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7888               uniqueNodes[0] = curNodes[0];
7889               uniqueNodes[1] = curNodes[2];
7890               uniqueNodes[2] = curNodes[3];
7891               uniqueNodes[3] = curNodes[1];
7892               uniqueNodes[4] = curNodes[6];
7893               uniqueNodes[5] = curNodes[7];
7894               isOk = true;
7895             }
7896             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7897               uniqueNodes[0] = curNodes[0];
7898               uniqueNodes[1] = curNodes[1];
7899               uniqueNodes[2] = curNodes[2];
7900               uniqueNodes[3] = curNodes[4];
7901               uniqueNodes[4] = curNodes[5];
7902               uniqueNodes[5] = curNodes[7];
7903               isOk = true;
7904             }
7905             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7906               uniqueNodes[0] = curNodes[0];
7907               uniqueNodes[1] = curNodes[1];
7908               uniqueNodes[2] = curNodes[3];
7909               uniqueNodes[3] = curNodes[4];
7910               uniqueNodes[4] = curNodes[2];
7911               uniqueNodes[5] = curNodes[7];
7912               isOk = true;
7913             }
7914             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7915               uniqueNodes[0] = curNodes[0];
7916               uniqueNodes[1] = curNodes[1];
7917               uniqueNodes[2] = curNodes[2];
7918               uniqueNodes[3] = curNodes[4];
7919               uniqueNodes[4] = curNodes[5];
7920               uniqueNodes[5] = curNodes[3];
7921               isOk = true;
7922             }
7923           }
7924           if(nbRepl==4) {
7925             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7926           }
7927           if(nbRepl==5) {
7928             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7929           }
7930           break;
7931         }
7932         //////////////////////////////////// HEXAHEDRON
7933         isOk = false;
7934         SMDS_VolumeTool hexa (elem);
7935         hexa.SetExternalNormal();
7936         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7937           //////////////////////// HEX ---> 1 tetrahedron
7938           for ( int iFace = 0; iFace < 6; iFace++ ) {
7939             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7940             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7941                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7942                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7943               // one face turns into a point ...
7944               int iOppFace = hexa.GetOppFaceIndex( iFace );
7945               ind = hexa.GetFaceNodesIndices( iOppFace );
7946               int nbStick = 0;
7947               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7948                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7949                   nbStick++;
7950               }
7951               if ( nbStick == 1 ) {
7952                 // ... and the opposite one - into a triangle.
7953                 // set a top node
7954                 ind = hexa.GetFaceNodesIndices( iFace );
7955                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7956                 isOk = true;
7957               }
7958               break;
7959             }
7960           }
7961         }
7962         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7963           //////////////////////// HEX ---> 1 prism
7964           int nbTria = 0, iTria[3];
7965           const int *ind; // indices of face nodes
7966           // look for triangular faces
7967           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7968             ind = hexa.GetFaceNodesIndices( iFace );
7969             TIDSortedNodeSet faceNodes;
7970             for ( iCur = 0; iCur < 4; iCur++ )
7971               faceNodes.insert( curNodes[ind[iCur]] );
7972             if ( faceNodes.size() == 3 )
7973               iTria[ nbTria++ ] = iFace;
7974           }
7975           // check if triangles are opposite
7976           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7977           {
7978             isOk = true;
7979             // set nodes of the bottom triangle
7980             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7981             vector<int> indB;
7982             for ( iCur = 0; iCur < 4; iCur++ )
7983               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7984                 indB.push_back( ind[iCur] );
7985             if ( !hexa.IsForward() )
7986               std::swap( indB[0], indB[2] );
7987             for ( iCur = 0; iCur < 3; iCur++ )
7988               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7989             // set nodes of the top triangle
7990             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7991             for ( iCur = 0; iCur < 3; ++iCur )
7992               for ( int j = 0; j < 4; ++j )
7993                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7994                 {
7995                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7996                   break;
7997                 }
7998           }
7999           break;
8000         }
8001         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8002           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8003           for ( int iFace = 0; iFace < 6; iFace++ ) {
8004             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8005             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8006                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8007                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8008               // one face turns into a point ...
8009               int iOppFace = hexa.GetOppFaceIndex( iFace );
8010               ind = hexa.GetFaceNodesIndices( iOppFace );
8011               int nbStick = 0;
8012               iUnique = 2;  // reverse a tetrahedron 1 bottom
8013               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8014                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8015                   nbStick++;
8016                 else if ( iUnique >= 0 )
8017                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8018               }
8019               if ( nbStick == 0 ) {
8020                 // ... and the opposite one is a quadrangle
8021                 // set a top node
8022                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8023                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8024                 nbUniqueNodes = 4;
8025                 // tetrahedron 2
8026                 SMDS_MeshElement* newElem =
8027                   aMesh->AddVolume(curNodes[ind[ 0 ]],
8028                                    curNodes[ind[ 3 ]],
8029                                    curNodes[ind[ 2 ]],
8030                                    curNodes[indTop[ 0 ]]);
8031                 myLastCreatedElems.Append(newElem);
8032                 if ( aShapeId )
8033                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8034                 isOk = true;
8035               }
8036               break;
8037             }
8038           }
8039         }
8040         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8041           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8042           // find indices of quad and tri faces
8043           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8044           for ( iFace = 0; iFace < 6; iFace++ ) {
8045             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8046             nodeSet.clear();
8047             for ( iCur = 0; iCur < 4; iCur++ )
8048               nodeSet.insert( curNodes[ind[ iCur ]] );
8049             nbUniqueNodes = nodeSet.size();
8050             if ( nbUniqueNodes == 3 )
8051               iTriFace[ nbTri++ ] = iFace;
8052             else if ( nbUniqueNodes == 4 )
8053               iQuadFace[ nbQuad++ ] = iFace;
8054           }
8055           if (nbQuad == 2 && nbTri == 4 &&
8056               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8057             // 2 opposite quadrangles stuck with a diagonal;
8058             // sample groups of merged indices: (0-4)(2-6)
8059             // --------------------------------------------> 2 tetrahedrons
8060             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8061             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8062             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8063             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8064                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8065               // stuck with 0-2 diagonal
8066               i0  = ind1[ 3 ];
8067               i1d = ind1[ 0 ];
8068               i2  = ind1[ 1 ];
8069               i3d = ind1[ 2 ];
8070               i0t = ind2[ 1 ];
8071               i2t = ind2[ 3 ];
8072             }
8073             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8074                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8075               // stuck with 1-3 diagonal
8076               i0  = ind1[ 0 ];
8077               i1d = ind1[ 1 ];
8078               i2  = ind1[ 2 ];
8079               i3d = ind1[ 3 ];
8080               i0t = ind2[ 0 ];
8081               i2t = ind2[ 1 ];
8082             }
8083             else {
8084               ASSERT(0);
8085             }
8086             // tetrahedron 1
8087             uniqueNodes[ 0 ] = curNodes [ i0 ];
8088             uniqueNodes[ 1 ] = curNodes [ i1d ];
8089             uniqueNodes[ 2 ] = curNodes [ i3d ];
8090             uniqueNodes[ 3 ] = curNodes [ i0t ];
8091             nbUniqueNodes = 4;
8092             // tetrahedron 2
8093             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8094                                                          curNodes[ i2 ],
8095                                                          curNodes[ i3d ],
8096                                                          curNodes[ i2t ]);
8097             myLastCreatedElems.Append(newElem);
8098             if ( aShapeId )
8099               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8100             isOk = true;
8101           }
8102           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8103                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8104             // --------------------------------------------> prism
8105             // find 2 opposite triangles
8106             nbUniqueNodes = 6;
8107             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8108               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8109                 // find indices of kept and replaced nodes
8110                 // and fill unique nodes of 2 opposite triangles
8111                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8112                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8113                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8114                 // fill unique nodes
8115                 iUnique = 0;
8116                 isOk = true;
8117                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8118                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8119                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8120                   if ( n == nInit ) {
8121                     // iCur of a linked node of the opposite face (make normals co-directed):
8122                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8123                     // check that correspondent corners of triangles are linked
8124                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8125                       isOk = false;
8126                     else {
8127                       uniqueNodes[ iUnique ] = n;
8128                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8129                       iUnique++;
8130                     }
8131                   }
8132                 }
8133                 break;
8134               }
8135             }
8136           }
8137         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8138         else
8139         {
8140           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8141         }
8142         break;
8143       } // HEXAHEDRON
8144
8145       default:
8146         isOk = false;
8147       } // switch ( nbNodes )
8148
8149     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8150
8151     if ( isOk ) { // the elem remains valid after sticking nodes
8152       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8153       {
8154         // Change nodes of polyedre
8155         const SMDS_VtkVolume* aPolyedre =
8156           dynamic_cast<const SMDS_VtkVolume*>( elem );
8157         if (aPolyedre) {
8158           int nbFaces = aPolyedre->NbFaces();
8159
8160           vector<const SMDS_MeshNode *> poly_nodes;
8161           vector<int> quantities (nbFaces);
8162
8163           for (int iface = 1; iface <= nbFaces; iface++) {
8164             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8165             quantities[iface - 1] = nbFaceNodes;
8166
8167             for (inode = 1; inode <= nbFaceNodes; inode++) {
8168               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8169
8170               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8171               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8172                 curNode = (*nnIt).second;
8173               }
8174               poly_nodes.push_back(curNode);
8175             }
8176           }
8177           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8178         }
8179       }
8180       else // replace non-polyhedron elements
8181       {
8182         const SMDSAbs_ElementType etyp = elem->GetType();
8183         const int elemId               = elem->GetID();
8184         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8185         uniqueNodes.resize(nbUniqueNodes);
8186
8187         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8188
8189         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8190         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8191         if ( sm && newElem )
8192           sm->AddElement( newElem );
8193         if ( elem != newElem )
8194           ReplaceElemInGroups( elem, newElem, aMesh );
8195       }
8196     }
8197     else {
8198       // Remove invalid regular element or invalid polygon
8199       rmElemIds.push_back( elem->GetID() );
8200     }
8201
8202   } // loop on elements
8203
8204   // Remove bad elements, then equal nodes (order important)
8205
8206   Remove( rmElemIds, false );
8207   Remove( rmNodeIds, true );
8208
8209 }
8210
8211
8212 // ========================================================
8213 // class   : SortableElement
8214 // purpose : allow sorting elements basing on their nodes
8215 // ========================================================
8216 class SortableElement : public set <const SMDS_MeshElement*>
8217 {
8218 public:
8219
8220   SortableElement( const SMDS_MeshElement* theElem )
8221   {
8222     myElem = theElem;
8223     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8224     while ( nodeIt->more() )
8225       this->insert( nodeIt->next() );
8226   }
8227
8228   const SMDS_MeshElement* Get() const
8229   { return myElem; }
8230
8231   void Set(const SMDS_MeshElement* e) const
8232   { myElem = e; }
8233
8234
8235 private:
8236   mutable const SMDS_MeshElement* myElem;
8237 };
8238
8239 //=======================================================================
8240 //function : FindEqualElements
8241 //purpose  : Return list of group of elements built on the same nodes.
8242 //           Search among theElements or in the whole mesh if theElements is empty
8243 //=======================================================================
8244
8245 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8246                                          TListOfListOfElementsID & theGroupsOfElementsID)
8247 {
8248   myLastCreatedElems.Clear();
8249   myLastCreatedNodes.Clear();
8250
8251   typedef map< SortableElement, int > TMapOfNodeSet;
8252   typedef list<int> TGroupOfElems;
8253
8254   if ( theElements.empty() )
8255   { // get all elements in the mesh
8256     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8257     while ( eIt->more() )
8258       theElements.insert( theElements.end(), eIt->next());
8259   }
8260
8261   vector< TGroupOfElems > arrayOfGroups;
8262   TGroupOfElems groupOfElems;
8263   TMapOfNodeSet mapOfNodeSet;
8264
8265   TIDSortedElemSet::iterator elemIt = theElements.begin();
8266   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8267     const SMDS_MeshElement* curElem = *elemIt;
8268     SortableElement SE(curElem);
8269     int ind = -1;
8270     // check uniqueness
8271     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8272     if( !(pp.second) ) {
8273       TMapOfNodeSet::iterator& itSE = pp.first;
8274       ind = (*itSE).second;
8275       arrayOfGroups[ind].push_back(curElem->GetID());
8276     }
8277     else {
8278       groupOfElems.clear();
8279       groupOfElems.push_back(curElem->GetID());
8280       arrayOfGroups.push_back(groupOfElems);
8281       i++;
8282     }
8283   }
8284
8285   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8286   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8287     groupOfElems = *groupIt;
8288     if ( groupOfElems.size() > 1 ) {
8289       groupOfElems.sort();
8290       theGroupsOfElementsID.push_back(groupOfElems);
8291     }
8292   }
8293 }
8294
8295 //=======================================================================
8296 //function : MergeElements
8297 //purpose  : In each given group, substitute all elements by the first one.
8298 //=======================================================================
8299
8300 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8301 {
8302   myLastCreatedElems.Clear();
8303   myLastCreatedNodes.Clear();
8304
8305   typedef list<int> TListOfIDs;
8306   TListOfIDs rmElemIds; // IDs of elems to remove
8307
8308   SMESHDS_Mesh* aMesh = GetMeshDS();
8309
8310   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8311   while ( groupsIt != theGroupsOfElementsID.end() ) {
8312     TListOfIDs& aGroupOfElemID = *groupsIt;
8313     aGroupOfElemID.sort();
8314     int elemIDToKeep = aGroupOfElemID.front();
8315     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8316     aGroupOfElemID.pop_front();
8317     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8318     while ( idIt != aGroupOfElemID.end() ) {
8319       int elemIDToRemove = *idIt;
8320       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8321       // add the kept element in groups of removed one (PAL15188)
8322       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8323       rmElemIds.push_back( elemIDToRemove );
8324       ++idIt;
8325     }
8326     ++groupsIt;
8327   }
8328
8329   Remove( rmElemIds, false );
8330 }
8331
8332 //=======================================================================
8333 //function : MergeEqualElements
8334 //purpose  : Remove all but one of elements built on the same nodes.
8335 //=======================================================================
8336
8337 void SMESH_MeshEditor::MergeEqualElements()
8338 {
8339   TIDSortedElemSet aMeshElements; /* empty input ==
8340                                      to merge equal elements in the whole mesh */
8341   TListOfListOfElementsID aGroupsOfElementsID;
8342   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8343   MergeElements(aGroupsOfElementsID);
8344 }
8345
8346 //=======================================================================
8347 //function : FindFaceInSet
8348 //purpose  : Return a face having linked nodes n1 and n2 and which is
8349 //           - not in avoidSet,
8350 //           - in elemSet provided that !elemSet.empty()
8351 //           i1 and i2 optionally returns indices of n1 and n2
8352 //=======================================================================
8353
8354 const SMDS_MeshElement*
8355 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8356                                 const SMDS_MeshNode*    n2,
8357                                 const TIDSortedElemSet& elemSet,
8358                                 const TIDSortedElemSet& avoidSet,
8359                                 int*                    n1ind,
8360                                 int*                    n2ind)
8361
8362 {
8363   int i1, i2;
8364   const SMDS_MeshElement* face = 0;
8365
8366   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8367   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8368   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8369   {
8370     //MESSAGE("in while ( invElemIt->more() && !face )");
8371     const SMDS_MeshElement* elem = invElemIt->next();
8372     if (avoidSet.count( elem ))
8373       continue;
8374     if ( !elemSet.empty() && !elemSet.count( elem ))
8375       continue;
8376     // index of n1
8377     i1 = elem->GetNodeIndex( n1 );
8378     // find a n2 linked to n1
8379     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8380     for ( int di = -1; di < 2 && !face; di += 2 )
8381     {
8382       i2 = (i1+di+nbN) % nbN;
8383       if ( elem->GetNode( i2 ) == n2 )
8384         face = elem;
8385     }
8386     if ( !face && elem->IsQuadratic())
8387     {
8388       // analysis for quadratic elements using all nodes
8389       const SMDS_VtkFace* F =
8390         dynamic_cast<const SMDS_VtkFace*>(elem);
8391       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8392       // use special nodes iterator
8393       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8394       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8395       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8396       {
8397         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8398         if ( n1 == prevN && n2 == n )
8399         {
8400           face = elem;
8401         }
8402         else if ( n2 == prevN && n1 == n )
8403         {
8404           face = elem; swap( i1, i2 );
8405         }
8406         prevN = n;
8407       }
8408     }
8409   }
8410   if ( n1ind ) *n1ind = i1;
8411   if ( n2ind ) *n2ind = i2;
8412   return face;
8413 }
8414
8415 //=======================================================================
8416 //function : findAdjacentFace
8417 //purpose  :
8418 //=======================================================================
8419
8420 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8421                                                 const SMDS_MeshNode* n2,
8422                                                 const SMDS_MeshElement* elem)
8423 {
8424   TIDSortedElemSet elemSet, avoidSet;
8425   if ( elem )
8426     avoidSet.insert ( elem );
8427   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8428 }
8429
8430 //=======================================================================
8431 //function : FindFreeBorder
8432 //purpose  :
8433 //=======================================================================
8434
8435 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8436
8437 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8438                                        const SMDS_MeshNode*             theSecondNode,
8439                                        const SMDS_MeshNode*             theLastNode,
8440                                        list< const SMDS_MeshNode* > &   theNodes,
8441                                        list< const SMDS_MeshElement* >& theFaces)
8442 {
8443   if ( !theFirstNode || !theSecondNode )
8444     return false;
8445   // find border face between theFirstNode and theSecondNode
8446   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8447   if ( !curElem )
8448     return false;
8449
8450   theFaces.push_back( curElem );
8451   theNodes.push_back( theFirstNode );
8452   theNodes.push_back( theSecondNode );
8453
8454   //vector<const SMDS_MeshNode*> nodes;
8455   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8456   TIDSortedElemSet foundElems;
8457   bool needTheLast = ( theLastNode != 0 );
8458
8459   while ( nStart != theLastNode ) {
8460     if ( nStart == theFirstNode )
8461       return !needTheLast;
8462
8463     // find all free border faces sharing form nStart
8464
8465     list< const SMDS_MeshElement* > curElemList;
8466     list< const SMDS_MeshNode* > nStartList;
8467     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8468     while ( invElemIt->more() ) {
8469       const SMDS_MeshElement* e = invElemIt->next();
8470       if ( e == curElem || foundElems.insert( e ).second ) {
8471         // get nodes
8472         int iNode = 0, nbNodes = e->NbNodes();
8473         //const SMDS_MeshNode* nodes[nbNodes+1];
8474         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8475
8476         if(e->IsQuadratic()) {
8477           const SMDS_VtkFace* F =
8478             dynamic_cast<const SMDS_VtkFace*>(e);
8479           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8480           // use special nodes iterator
8481           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8482           while( anIter->more() ) {
8483             nodes[ iNode++ ] = cast2Node(anIter->next());
8484           }
8485         }
8486         else {
8487           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8488           while ( nIt->more() )
8489             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8490         }
8491         nodes[ iNode ] = nodes[ 0 ];
8492         // check 2 links
8493         for ( iNode = 0; iNode < nbNodes; iNode++ )
8494           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8495                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8496               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8497           {
8498             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8499             curElemList.push_back( e );
8500           }
8501       }
8502     }
8503     // analyse the found
8504
8505     int nbNewBorders = curElemList.size();
8506     if ( nbNewBorders == 0 ) {
8507       // no free border furthermore
8508       return !needTheLast;
8509     }
8510     else if ( nbNewBorders == 1 ) {
8511       // one more element found
8512       nIgnore = nStart;
8513       nStart = nStartList.front();
8514       curElem = curElemList.front();
8515       theFaces.push_back( curElem );
8516       theNodes.push_back( nStart );
8517     }
8518     else {
8519       // several continuations found
8520       list< const SMDS_MeshElement* >::iterator curElemIt;
8521       list< const SMDS_MeshNode* >::iterator nStartIt;
8522       // check if one of them reached the last node
8523       if ( needTheLast ) {
8524         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8525              curElemIt!= curElemList.end();
8526              curElemIt++, nStartIt++ )
8527           if ( *nStartIt == theLastNode ) {
8528             theFaces.push_back( *curElemIt );
8529             theNodes.push_back( *nStartIt );
8530             return true;
8531           }
8532       }
8533       // find the best free border by the continuations
8534       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8535       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8536       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8537            curElemIt!= curElemList.end();
8538            curElemIt++, nStartIt++ )
8539       {
8540         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8541         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8542         // find one more free border
8543         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8544           cNL->clear();
8545           cFL->clear();
8546         }
8547         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8548           // choice: clear a worse one
8549           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8550           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8551           contNodes[ iWorse ].clear();
8552           contFaces[ iWorse ].clear();
8553         }
8554       }
8555       if ( contNodes[0].empty() && contNodes[1].empty() )
8556         return false;
8557
8558       // append the best free border
8559       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8560       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8561       theNodes.pop_back(); // remove nIgnore
8562       theNodes.pop_back(); // remove nStart
8563       theFaces.pop_back(); // remove curElem
8564       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8565       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8566       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8567       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8568       return true;
8569
8570     } // several continuations found
8571   } // while ( nStart != theLastNode )
8572
8573   return true;
8574 }
8575
8576 //=======================================================================
8577 //function : CheckFreeBorderNodes
8578 //purpose  : Return true if the tree nodes are on a free border
8579 //=======================================================================
8580
8581 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8582                                             const SMDS_MeshNode* theNode2,
8583                                             const SMDS_MeshNode* theNode3)
8584 {
8585   list< const SMDS_MeshNode* > nodes;
8586   list< const SMDS_MeshElement* > faces;
8587   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8588 }
8589
8590 //=======================================================================
8591 //function : SewFreeBorder
8592 //purpose  :
8593 //=======================================================================
8594
8595 SMESH_MeshEditor::Sew_Error
8596 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8597                                  const SMDS_MeshNode* theBordSecondNode,
8598                                  const SMDS_MeshNode* theBordLastNode,
8599                                  const SMDS_MeshNode* theSideFirstNode,
8600                                  const SMDS_MeshNode* theSideSecondNode,
8601                                  const SMDS_MeshNode* theSideThirdNode,
8602                                  const bool           theSideIsFreeBorder,
8603                                  const bool           toCreatePolygons,
8604                                  const bool           toCreatePolyedrs)
8605 {
8606   myLastCreatedElems.Clear();
8607   myLastCreatedNodes.Clear();
8608
8609   MESSAGE("::SewFreeBorder()");
8610   Sew_Error aResult = SEW_OK;
8611
8612   // ====================================
8613   //    find side nodes and elements
8614   // ====================================
8615
8616   list< const SMDS_MeshNode* > nSide[ 2 ];
8617   list< const SMDS_MeshElement* > eSide[ 2 ];
8618   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8619   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8620
8621   // Free border 1
8622   // --------------
8623   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8624                       nSide[0], eSide[0])) {
8625     MESSAGE(" Free Border 1 not found " );
8626     aResult = SEW_BORDER1_NOT_FOUND;
8627   }
8628   if (theSideIsFreeBorder) {
8629     // Free border 2
8630     // --------------
8631     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8632                         nSide[1], eSide[1])) {
8633       MESSAGE(" Free Border 2 not found " );
8634       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8635     }
8636   }
8637   if ( aResult != SEW_OK )
8638     return aResult;
8639
8640   if (!theSideIsFreeBorder) {
8641     // Side 2
8642     // --------------
8643
8644     // -------------------------------------------------------------------------
8645     // Algo:
8646     // 1. If nodes to merge are not coincident, move nodes of the free border
8647     //    from the coord sys defined by the direction from the first to last
8648     //    nodes of the border to the correspondent sys of the side 2
8649     // 2. On the side 2, find the links most co-directed with the correspondent
8650     //    links of the free border
8651     // -------------------------------------------------------------------------
8652
8653     // 1. Since sewing may break if there are volumes to split on the side 2,
8654     //    we wont move nodes but just compute new coordinates for them
8655     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8656     TNodeXYZMap nBordXYZ;
8657     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8658     list< const SMDS_MeshNode* >::iterator nBordIt;
8659
8660     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8661     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8662     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8663     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8664     double tol2 = 1.e-8;
8665     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8666     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8667       // Need node movement.
8668
8669       // find X and Z axes to create trsf
8670       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8671       gp_Vec X = Zs ^ Zb;
8672       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8673         // Zb || Zs
8674         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8675
8676       // coord systems
8677       gp_Ax3 toBordAx( Pb1, Zb, X );
8678       gp_Ax3 fromSideAx( Ps1, Zs, X );
8679       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8680       // set trsf
8681       gp_Trsf toBordSys, fromSide2Sys;
8682       toBordSys.SetTransformation( toBordAx );
8683       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8684       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8685
8686       // move
8687       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8688         const SMDS_MeshNode* n = *nBordIt;
8689         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8690         toBordSys.Transforms( xyz );
8691         fromSide2Sys.Transforms( xyz );
8692         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8693       }
8694     }
8695     else {
8696       // just insert nodes XYZ in the nBordXYZ map
8697       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8698         const SMDS_MeshNode* n = *nBordIt;
8699         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8700       }
8701     }
8702
8703     // 2. On the side 2, find the links most co-directed with the correspondent
8704     //    links of the free border
8705
8706     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8707     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8708     sideNodes.push_back( theSideFirstNode );
8709
8710     bool hasVolumes = false;
8711     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8712     set<long> foundSideLinkIDs, checkedLinkIDs;
8713     SMDS_VolumeTool volume;
8714     //const SMDS_MeshNode* faceNodes[ 4 ];
8715
8716     const SMDS_MeshNode*    sideNode;
8717     const SMDS_MeshElement* sideElem;
8718     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8719     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8720     nBordIt = bordNodes.begin();
8721     nBordIt++;
8722     // border node position and border link direction to compare with
8723     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8724     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8725     // choose next side node by link direction or by closeness to
8726     // the current border node:
8727     bool searchByDir = ( *nBordIt != theBordLastNode );
8728     do {
8729       // find the next node on the Side 2
8730       sideNode = 0;
8731       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8732       long linkID;
8733       checkedLinkIDs.clear();
8734       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8735
8736       // loop on inverse elements of current node (prevSideNode) on the Side 2
8737       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8738       while ( invElemIt->more() )
8739       {
8740         const SMDS_MeshElement* elem = invElemIt->next();
8741         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8742         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8743         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8744         bool isVolume = volume.Set( elem );
8745         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8746         if ( isVolume ) // --volume
8747           hasVolumes = true;
8748         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8749           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8750           if(elem->IsQuadratic()) {
8751             const SMDS_VtkFace* F =
8752               dynamic_cast<const SMDS_VtkFace*>(elem);
8753             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8754             // use special nodes iterator
8755             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8756             while( anIter->more() ) {
8757               nodes[ iNode ] = cast2Node(anIter->next());
8758               if ( nodes[ iNode++ ] == prevSideNode )
8759                 iPrevNode = iNode - 1;
8760             }
8761           }
8762           else {
8763             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8764             while ( nIt->more() ) {
8765               nodes[ iNode ] = cast2Node( nIt->next() );
8766               if ( nodes[ iNode++ ] == prevSideNode )
8767                 iPrevNode = iNode - 1;
8768             }
8769           }
8770           // there are 2 links to check
8771           nbNodes = 2;
8772         }
8773         else // --edge
8774           continue;
8775         // loop on links, to be precise, on the second node of links
8776         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8777           const SMDS_MeshNode* n = nodes[ iNode ];
8778           if ( isVolume ) {
8779             if ( !volume.IsLinked( n, prevSideNode ))
8780               continue;
8781           }
8782           else {
8783             if ( iNode ) // a node before prevSideNode
8784               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8785             else         // a node after prevSideNode
8786               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8787           }
8788           // check if this link was already used
8789           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8790           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8791           if (!isJustChecked &&
8792               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8793           {
8794             // test a link geometrically
8795             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8796             bool linkIsBetter = false;
8797             double dot = 0.0, dist = 0.0;
8798             if ( searchByDir ) { // choose most co-directed link
8799               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8800               linkIsBetter = ( dot > maxDot );
8801             }
8802             else { // choose link with the node closest to bordPos
8803               dist = ( nextXYZ - bordPos ).SquareModulus();
8804               linkIsBetter = ( dist < minDist );
8805             }
8806             if ( linkIsBetter ) {
8807               maxDot = dot;
8808               minDist = dist;
8809               linkID = iLink;
8810               sideNode = n;
8811               sideElem = elem;
8812             }
8813           }
8814         }
8815       } // loop on inverse elements of prevSideNode
8816
8817       if ( !sideNode ) {
8818         MESSAGE(" Cant find path by links of the Side 2 ");
8819         return SEW_BAD_SIDE_NODES;
8820       }
8821       sideNodes.push_back( sideNode );
8822       sideElems.push_back( sideElem );
8823       foundSideLinkIDs.insert ( linkID );
8824       prevSideNode = sideNode;
8825
8826       if ( *nBordIt == theBordLastNode )
8827         searchByDir = false;
8828       else {
8829         // find the next border link to compare with
8830         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8831         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8832         // move to next border node if sideNode is before forward border node (bordPos)
8833         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8834           prevBordNode = *nBordIt;
8835           nBordIt++;
8836           bordPos = nBordXYZ[ *nBordIt ];
8837           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8838           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8839         }
8840       }
8841     }
8842     while ( sideNode != theSideSecondNode );
8843
8844     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8845       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8846       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8847     }
8848   } // end nodes search on the side 2
8849
8850   // ============================
8851   // sew the border to the side 2
8852   // ============================
8853
8854   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8855   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8856
8857   TListOfListOfNodes nodeGroupsToMerge;
8858   if ( nbNodes[0] == nbNodes[1] ||
8859        ( theSideIsFreeBorder && !theSideThirdNode)) {
8860
8861     // all nodes are to be merged
8862
8863     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8864          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8865          nIt[0]++, nIt[1]++ )
8866     {
8867       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8868       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8869       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8870     }
8871   }
8872   else {
8873
8874     // insert new nodes into the border and the side to get equal nb of segments
8875
8876     // get normalized parameters of nodes on the borders
8877     //double param[ 2 ][ maxNbNodes ];
8878     double* param[ 2 ];
8879     param[0] = new double [ maxNbNodes ];
8880     param[1] = new double [ maxNbNodes ];
8881     int iNode, iBord;
8882     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8883       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8884       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8885       const SMDS_MeshNode* nPrev = *nIt;
8886       double bordLength = 0;
8887       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8888         const SMDS_MeshNode* nCur = *nIt;
8889         gp_XYZ segment (nCur->X() - nPrev->X(),
8890                         nCur->Y() - nPrev->Y(),
8891                         nCur->Z() - nPrev->Z());
8892         double segmentLen = segment.Modulus();
8893         bordLength += segmentLen;
8894         param[ iBord ][ iNode ] = bordLength;
8895         nPrev = nCur;
8896       }
8897       // normalize within [0,1]
8898       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8899         param[ iBord ][ iNode ] /= bordLength;
8900       }
8901     }
8902
8903     // loop on border segments
8904     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8905     int i[ 2 ] = { 0, 0 };
8906     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8907     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8908
8909     TElemOfNodeListMap insertMap;
8910     TElemOfNodeListMap::iterator insertMapIt;
8911     // insertMap is
8912     // key:   elem to insert nodes into
8913     // value: 2 nodes to insert between + nodes to be inserted
8914     do {
8915       bool next[ 2 ] = { false, false };
8916
8917       // find min adjacent segment length after sewing
8918       double nextParam = 10., prevParam = 0;
8919       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8920         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8921           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8922         if ( i[ iBord ] > 0 )
8923           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8924       }
8925       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8926       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8927       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8928
8929       // choose to insert or to merge nodes
8930       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8931       if ( Abs( du ) <= minSegLen * 0.2 ) {
8932         // merge
8933         // ------
8934         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8935         const SMDS_MeshNode* n0 = *nIt[0];
8936         const SMDS_MeshNode* n1 = *nIt[1];
8937         nodeGroupsToMerge.back().push_back( n1 );
8938         nodeGroupsToMerge.back().push_back( n0 );
8939         // position of node of the border changes due to merge
8940         param[ 0 ][ i[0] ] += du;
8941         // move n1 for the sake of elem shape evaluation during insertion.
8942         // n1 will be removed by MergeNodes() anyway
8943         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8944         next[0] = next[1] = true;
8945       }
8946       else {
8947         // insert
8948         // ------
8949         int intoBord = ( du < 0 ) ? 0 : 1;
8950         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8951         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8952         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8953         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8954         if ( intoBord == 1 ) {
8955           // move node of the border to be on a link of elem of the side
8956           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8957           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8958           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8959           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8960           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8961         }
8962         insertMapIt = insertMap.find( elem );
8963         bool notFound = ( insertMapIt == insertMap.end() );
8964         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8965         if ( otherLink ) {
8966           // insert into another link of the same element:
8967           // 1. perform insertion into the other link of the elem
8968           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8969           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8970           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8971           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8972           // 2. perform insertion into the link of adjacent faces
8973           while (true) {
8974             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8975             if ( adjElem )
8976               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8977             else
8978               break;
8979           }
8980           if (toCreatePolyedrs) {
8981             // perform insertion into the links of adjacent volumes
8982             UpdateVolumes(n12, n22, nodeList);
8983           }
8984           // 3. find an element appeared on n1 and n2 after the insertion
8985           insertMap.erase( elem );
8986           elem = findAdjacentFace( n1, n2, 0 );
8987         }
8988         if ( notFound || otherLink ) {
8989           // add element and nodes of the side into the insertMap
8990           insertMapIt = insertMap.insert
8991             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8992           (*insertMapIt).second.push_back( n1 );
8993           (*insertMapIt).second.push_back( n2 );
8994         }
8995         // add node to be inserted into elem
8996         (*insertMapIt).second.push_back( nIns );
8997         next[ 1 - intoBord ] = true;
8998       }
8999
9000       // go to the next segment
9001       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9002         if ( next[ iBord ] ) {
9003           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9004             eIt[ iBord ]++;
9005           nPrev[ iBord ] = *nIt[ iBord ];
9006           nIt[ iBord ]++; i[ iBord ]++;
9007         }
9008       }
9009     }
9010     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9011
9012     // perform insertion of nodes into elements
9013
9014     for (insertMapIt = insertMap.begin();
9015          insertMapIt != insertMap.end();
9016          insertMapIt++ )
9017     {
9018       const SMDS_MeshElement* elem = (*insertMapIt).first;
9019       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9020       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9021       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9022
9023       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9024
9025       if ( !theSideIsFreeBorder ) {
9026         // look for and insert nodes into the faces adjacent to elem
9027         while (true) {
9028           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9029           if ( adjElem )
9030             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9031           else
9032             break;
9033         }
9034       }
9035       if (toCreatePolyedrs) {
9036         // perform insertion into the links of adjacent volumes
9037         UpdateVolumes(n1, n2, nodeList);
9038       }
9039     }
9040
9041     delete param[0];
9042     delete param[1];
9043   } // end: insert new nodes
9044
9045   MergeNodes ( nodeGroupsToMerge );
9046
9047   return aResult;
9048 }
9049
9050 //=======================================================================
9051 //function : InsertNodesIntoLink
9052 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9053 //           and theBetweenNode2 and split theElement
9054 //=======================================================================
9055
9056 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9057                                            const SMDS_MeshNode*        theBetweenNode1,
9058                                            const SMDS_MeshNode*        theBetweenNode2,
9059                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9060                                            const bool                  toCreatePoly)
9061 {
9062   if ( theFace->GetType() != SMDSAbs_Face ) return;
9063
9064   // find indices of 2 link nodes and of the rest nodes
9065   int iNode = 0, il1, il2, i3, i4;
9066   il1 = il2 = i3 = i4 = -1;
9067   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9068   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9069
9070   if(theFace->IsQuadratic()) {
9071     const SMDS_VtkFace* F =
9072       dynamic_cast<const SMDS_VtkFace*>(theFace);
9073     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9074     // use special nodes iterator
9075     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9076     while( anIter->more() ) {
9077       const SMDS_MeshNode* n = cast2Node(anIter->next());
9078       if ( n == theBetweenNode1 )
9079         il1 = iNode;
9080       else if ( n == theBetweenNode2 )
9081         il2 = iNode;
9082       else if ( i3 < 0 )
9083         i3 = iNode;
9084       else
9085         i4 = iNode;
9086       nodes[ iNode++ ] = n;
9087     }
9088   }
9089   else {
9090     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9091     while ( nodeIt->more() ) {
9092       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9093       if ( n == theBetweenNode1 )
9094         il1 = iNode;
9095       else if ( n == theBetweenNode2 )
9096         il2 = iNode;
9097       else if ( i3 < 0 )
9098         i3 = iNode;
9099       else
9100         i4 = iNode;
9101       nodes[ iNode++ ] = n;
9102     }
9103   }
9104   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9105     return ;
9106
9107   // arrange link nodes to go one after another regarding the face orientation
9108   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9109   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9110   if ( reverse ) {
9111     iNode = il1;
9112     il1 = il2;
9113     il2 = iNode;
9114     aNodesToInsert.reverse();
9115   }
9116   // check that not link nodes of a quadrangles are in good order
9117   int nbFaceNodes = theFace->NbNodes();
9118   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9119     iNode = i3;
9120     i3 = i4;
9121     i4 = iNode;
9122   }
9123
9124   if (toCreatePoly || theFace->IsPoly()) {
9125
9126     iNode = 0;
9127     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9128
9129     // add nodes of face up to first node of link
9130     bool isFLN = false;
9131
9132     if(theFace->IsQuadratic()) {
9133       const SMDS_VtkFace* F =
9134         dynamic_cast<const SMDS_VtkFace*>(theFace);
9135       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9136       // use special nodes iterator
9137       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9138       while( anIter->more()  && !isFLN ) {
9139         const SMDS_MeshNode* n = cast2Node(anIter->next());
9140         poly_nodes[iNode++] = n;
9141         if (n == nodes[il1]) {
9142           isFLN = true;
9143         }
9144       }
9145       // add nodes to insert
9146       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9147       for (; nIt != aNodesToInsert.end(); nIt++) {
9148         poly_nodes[iNode++] = *nIt;
9149       }
9150       // add nodes of face starting from last node of link
9151       while ( anIter->more() ) {
9152         poly_nodes[iNode++] = cast2Node(anIter->next());
9153       }
9154     }
9155     else {
9156       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9157       while ( nodeIt->more() && !isFLN ) {
9158         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9159         poly_nodes[iNode++] = n;
9160         if (n == nodes[il1]) {
9161           isFLN = true;
9162         }
9163       }
9164       // add nodes to insert
9165       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9166       for (; nIt != aNodesToInsert.end(); nIt++) {
9167         poly_nodes[iNode++] = *nIt;
9168       }
9169       // add nodes of face starting from last node of link
9170       while ( nodeIt->more() ) {
9171         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9172         poly_nodes[iNode++] = n;
9173       }
9174     }
9175
9176     // edit or replace the face
9177     SMESHDS_Mesh *aMesh = GetMeshDS();
9178
9179     if (theFace->IsPoly()) {
9180       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9181     }
9182     else {
9183       int aShapeId = FindShape( theFace );
9184
9185       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9186       myLastCreatedElems.Append(newElem);
9187       if ( aShapeId && newElem )
9188         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9189
9190       aMesh->RemoveElement(theFace);
9191     }
9192     return;
9193   }
9194
9195   SMESHDS_Mesh *aMesh = GetMeshDS();
9196   if( !theFace->IsQuadratic() ) {
9197
9198     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9199     int nbLinkNodes = 2 + aNodesToInsert.size();
9200     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9201     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9202     linkNodes[ 0 ] = nodes[ il1 ];
9203     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9204     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9205     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9206       linkNodes[ iNode++ ] = *nIt;
9207     }
9208     // decide how to split a quadrangle: compare possible variants
9209     // and choose which of splits to be a quadrangle
9210     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9211     if ( nbFaceNodes == 3 ) {
9212       iBestQuad = nbSplits;
9213       i4 = i3;
9214     }
9215     else if ( nbFaceNodes == 4 ) {
9216       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9217       double aBestRate = DBL_MAX;
9218       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9219         i1 = 0; i2 = 1;
9220         double aBadRate = 0;
9221         // evaluate elements quality
9222         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9223           if ( iSplit == iQuad ) {
9224             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9225                                    linkNodes[ i2++ ],
9226                                    nodes[ i3 ],
9227                                    nodes[ i4 ]);
9228             aBadRate += getBadRate( &quad, aCrit );
9229           }
9230           else {
9231             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9232                                    linkNodes[ i2++ ],
9233                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9234             aBadRate += getBadRate( &tria, aCrit );
9235           }
9236         }
9237         // choice
9238         if ( aBadRate < aBestRate ) {
9239           iBestQuad = iQuad;
9240           aBestRate = aBadRate;
9241         }
9242       }
9243     }
9244
9245     // create new elements
9246     int aShapeId = FindShape( theFace );
9247
9248     i1 = 0; i2 = 1;
9249     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9250       SMDS_MeshElement* newElem = 0;
9251       if ( iSplit == iBestQuad )
9252         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9253                                   linkNodes[ i2++ ],
9254                                   nodes[ i3 ],
9255                                   nodes[ i4 ]);
9256       else
9257         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9258                                   linkNodes[ i2++ ],
9259                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9260       myLastCreatedElems.Append(newElem);
9261       if ( aShapeId && newElem )
9262         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9263     }
9264
9265     // change nodes of theFace
9266     const SMDS_MeshNode* newNodes[ 4 ];
9267     newNodes[ 0 ] = linkNodes[ i1 ];
9268     newNodes[ 1 ] = linkNodes[ i2 ];
9269     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9270     newNodes[ 3 ] = nodes[ i4 ];
9271     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9272     const SMDS_MeshElement* newElem = 0;
9273     if (iSplit == iBestQuad)
9274       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9275     else
9276       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9277     myLastCreatedElems.Append(newElem);
9278     if ( aShapeId && newElem )
9279       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9280 } // end if(!theFace->IsQuadratic())
9281   else { // theFace is quadratic
9282     // we have to split theFace on simple triangles and one simple quadrangle
9283     int tmp = il1/2;
9284     int nbshift = tmp*2;
9285     // shift nodes in nodes[] by nbshift
9286     int i,j;
9287     for(i=0; i<nbshift; i++) {
9288       const SMDS_MeshNode* n = nodes[0];
9289       for(j=0; j<nbFaceNodes-1; j++) {
9290         nodes[j] = nodes[j+1];
9291       }
9292       nodes[nbFaceNodes-1] = n;
9293     }
9294     il1 = il1 - nbshift;
9295     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9296     //   n0      n1     n2    n0      n1     n2
9297     //     +-----+-----+        +-----+-----+
9298     //      \         /         |           |
9299     //       \       /          |           |
9300     //      n5+     +n3       n7+           +n3
9301     //         \   /            |           |
9302     //          \ /             |           |
9303     //           +              +-----+-----+
9304     //           n4           n6      n5     n4
9305
9306     // create new elements
9307     int aShapeId = FindShape( theFace );
9308
9309     int n1,n2,n3;
9310     if(nbFaceNodes==6) { // quadratic triangle
9311       SMDS_MeshElement* newElem =
9312         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9313       myLastCreatedElems.Append(newElem);
9314       if ( aShapeId && newElem )
9315         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9316       if(theFace->IsMediumNode(nodes[il1])) {
9317         // create quadrangle
9318         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9319         myLastCreatedElems.Append(newElem);
9320         if ( aShapeId && newElem )
9321           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9322         n1 = 1;
9323         n2 = 2;
9324         n3 = 3;
9325       }
9326       else {
9327         // create quadrangle
9328         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9329         myLastCreatedElems.Append(newElem);
9330         if ( aShapeId && newElem )
9331           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9332         n1 = 0;
9333         n2 = 1;
9334         n3 = 5;
9335       }
9336     }
9337     else { // nbFaceNodes==8 - quadratic quadrangle
9338       SMDS_MeshElement* newElem =
9339         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9340       myLastCreatedElems.Append(newElem);
9341       if ( aShapeId && newElem )
9342         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9343       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9344       myLastCreatedElems.Append(newElem);
9345       if ( aShapeId && newElem )
9346         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9347       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9348       myLastCreatedElems.Append(newElem);
9349       if ( aShapeId && newElem )
9350         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9351       if(theFace->IsMediumNode(nodes[il1])) {
9352         // create quadrangle
9353         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9354         myLastCreatedElems.Append(newElem);
9355         if ( aShapeId && newElem )
9356           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9357         n1 = 1;
9358         n2 = 2;
9359         n3 = 3;
9360       }
9361       else {
9362         // create quadrangle
9363         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9364         myLastCreatedElems.Append(newElem);
9365         if ( aShapeId && newElem )
9366           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9367         n1 = 0;
9368         n2 = 1;
9369         n3 = 7;
9370       }
9371     }
9372     // create needed triangles using n1,n2,n3 and inserted nodes
9373     int nbn = 2 + aNodesToInsert.size();
9374     //const SMDS_MeshNode* aNodes[nbn];
9375     vector<const SMDS_MeshNode*> aNodes(nbn);
9376     aNodes[0] = nodes[n1];
9377     aNodes[nbn-1] = nodes[n2];
9378     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9379     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9380       aNodes[iNode++] = *nIt;
9381     }
9382     for(i=1; i<nbn; i++) {
9383       SMDS_MeshElement* newElem =
9384         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9385       myLastCreatedElems.Append(newElem);
9386       if ( aShapeId && newElem )
9387         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9388     }
9389   }
9390   // remove old face
9391   aMesh->RemoveElement(theFace);
9392 }
9393
9394 //=======================================================================
9395 //function : UpdateVolumes
9396 //purpose  :
9397 //=======================================================================
9398 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9399                                       const SMDS_MeshNode*        theBetweenNode2,
9400                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9401 {
9402   myLastCreatedElems.Clear();
9403   myLastCreatedNodes.Clear();
9404
9405   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9406   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9407     const SMDS_MeshElement* elem = invElemIt->next();
9408
9409     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9410     SMDS_VolumeTool aVolume (elem);
9411     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9412       continue;
9413
9414     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9415     int iface, nbFaces = aVolume.NbFaces();
9416     vector<const SMDS_MeshNode *> poly_nodes;
9417     vector<int> quantities (nbFaces);
9418
9419     for (iface = 0; iface < nbFaces; iface++) {
9420       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9421       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9422       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9423
9424       for (int inode = 0; inode < nbFaceNodes; inode++) {
9425         poly_nodes.push_back(faceNodes[inode]);
9426
9427         if (nbInserted == 0) {
9428           if (faceNodes[inode] == theBetweenNode1) {
9429             if (faceNodes[inode + 1] == theBetweenNode2) {
9430               nbInserted = theNodesToInsert.size();
9431
9432               // add nodes to insert
9433               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9434               for (; nIt != theNodesToInsert.end(); nIt++) {
9435                 poly_nodes.push_back(*nIt);
9436               }
9437             }
9438           }
9439           else if (faceNodes[inode] == theBetweenNode2) {
9440             if (faceNodes[inode + 1] == theBetweenNode1) {
9441               nbInserted = theNodesToInsert.size();
9442
9443               // add nodes to insert in reversed order
9444               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9445               nIt--;
9446               for (; nIt != theNodesToInsert.begin(); nIt--) {
9447                 poly_nodes.push_back(*nIt);
9448               }
9449               poly_nodes.push_back(*nIt);
9450             }
9451           }
9452           else {
9453           }
9454         }
9455       }
9456       quantities[iface] = nbFaceNodes + nbInserted;
9457     }
9458
9459     // Replace or update the volume
9460     SMESHDS_Mesh *aMesh = GetMeshDS();
9461
9462     if (elem->IsPoly()) {
9463       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9464
9465     }
9466     else {
9467       int aShapeId = FindShape( elem );
9468
9469       SMDS_MeshElement* newElem =
9470         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9471       myLastCreatedElems.Append(newElem);
9472       if (aShapeId && newElem)
9473         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9474
9475       aMesh->RemoveElement(elem);
9476     }
9477   }
9478 }
9479
9480 namespace
9481 {
9482   //================================================================================
9483   /*!
9484    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9485    */
9486   //================================================================================
9487
9488   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9489                            vector<const SMDS_MeshNode *> & nodes,
9490                            vector<int> &                   nbNodeInFaces )
9491   {
9492     nodes.clear();
9493     nbNodeInFaces.clear();
9494     SMDS_VolumeTool vTool ( elem );
9495     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9496     {
9497       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9498       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9499       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9500     }
9501   }
9502 }
9503
9504 //=======================================================================
9505 /*!
9506  * \brief Convert elements contained in a submesh to quadratic
9507  * \return int - nb of checked elements
9508  */
9509 //=======================================================================
9510
9511 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9512                                              SMESH_MesherHelper& theHelper,
9513                                              const bool          theForce3d)
9514 {
9515   int nbElem = 0;
9516   if( !theSm ) return nbElem;
9517
9518   vector<int> nbNodeInFaces;
9519   vector<const SMDS_MeshNode *> nodes;
9520   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9521   while(ElemItr->more())
9522   {
9523     nbElem++;
9524     const SMDS_MeshElement* elem = ElemItr->next();
9525     if( !elem || elem->IsQuadratic() ) continue;
9526
9527     // get elem data needed to re-create it
9528     //
9529     const int id                        = elem->GetID();
9530     const int nbNodes                   = elem->NbNodes();
9531     const SMDSAbs_ElementType aType     = elem->GetType();
9532     const SMDSAbs_EntityType  aGeomType = elem->GetEntityType();
9533     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9534     if ( aGeomType == SMDSEntity_Polyhedra )
9535       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9536     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9537       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9538
9539     // remove a linear element
9540     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9541
9542     const SMDS_MeshElement* NewElem = 0;
9543
9544     switch( aType )
9545     {
9546     case SMDSAbs_Edge :
9547       {
9548         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9549         break;
9550       }
9551     case SMDSAbs_Face :
9552       {
9553         switch(nbNodes)
9554         {
9555         case 3:
9556           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9557           break;
9558         case 4:
9559           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9560           break;
9561         default:
9562           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9563           continue;
9564         }
9565         break;
9566       }
9567     case SMDSAbs_Volume :
9568       {
9569         switch( aGeomType )
9570         {
9571         case SMDSEntity_Tetra:
9572           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9573           break;
9574         case SMDSEntity_Pyramid:
9575           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9576           break;
9577         case SMDSEntity_Penta:
9578           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9579           break;
9580         case SMDSEntity_Hexa:
9581           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9582                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9583           break;
9584         case SMDSEntity_Hexagonal_Prism:
9585         default:
9586           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9587         }
9588         break;
9589       }
9590     default :
9591       continue;
9592     }
9593     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9594     if( NewElem )
9595       theSm->AddElement( NewElem );
9596   }
9597   return nbElem;
9598 }
9599
9600 //=======================================================================
9601 //function : ConvertToQuadratic
9602 //purpose  :
9603 //=======================================================================
9604
9605 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9606 {
9607   SMESHDS_Mesh* meshDS = GetMeshDS();
9608
9609   SMESH_MesherHelper aHelper(*myMesh);
9610   aHelper.SetIsQuadratic( true );
9611
9612   int nbCheckedElems = 0;
9613   if ( myMesh->HasShapeToMesh() )
9614   {
9615     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9616     {
9617       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9618       while ( smIt->more() ) {
9619         SMESH_subMesh* sm = smIt->next();
9620         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9621           aHelper.SetSubShape( sm->GetSubShape() );
9622           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9623         }
9624       }
9625     }
9626   }
9627   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9628   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9629   {
9630     SMESHDS_SubMesh *smDS = 0;
9631     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9632     while(aEdgeItr->more())
9633     {
9634       const SMDS_MeshEdge* edge = aEdgeItr->next();
9635       if(edge && !edge->IsQuadratic())
9636       {
9637         int id = edge->GetID();
9638         //MESSAGE("edge->GetID() " << id);
9639         const SMDS_MeshNode* n1 = edge->GetNode(0);
9640         const SMDS_MeshNode* n2 = edge->GetNode(1);
9641
9642         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9643
9644         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9645         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9646       }
9647     }
9648     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9649     while(aFaceItr->more())
9650     {
9651       const SMDS_MeshFace* face = aFaceItr->next();
9652       if(!face || face->IsQuadratic() ) continue;
9653
9654       const int id = face->GetID();
9655       const SMDSAbs_EntityType type = face->GetEntityType();
9656       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9657
9658       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9659
9660       SMDS_MeshFace * NewFace = 0;
9661       switch( type )
9662       {
9663       case SMDSEntity_Triangle:
9664         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9665         break;
9666       case SMDSEntity_Quadrangle:
9667         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9668         break;
9669       default:
9670         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9671       }
9672       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9673     }
9674     vector<int> nbNodeInFaces;
9675     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9676     while(aVolumeItr->more())
9677     {
9678       const SMDS_MeshVolume* volume = aVolumeItr->next();
9679       if(!volume || volume->IsQuadratic() ) continue;
9680
9681       const int id = volume->GetID();
9682       const SMDSAbs_EntityType type = volume->GetEntityType();
9683       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9684       if ( type == SMDSEntity_Polyhedra )
9685         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9686       else if ( type == SMDSEntity_Hexagonal_Prism )
9687         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9688
9689       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9690
9691       SMDS_MeshVolume * NewVolume = 0;
9692       switch ( type )
9693       {
9694       case SMDSEntity_Tetra:
9695         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9696         break;
9697       case SMDSEntity_Hexa:
9698         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9699                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9700         break;
9701       case SMDSEntity_Pyramid:
9702         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9703                                       nodes[3], nodes[4], id, theForce3d);
9704         break;
9705       case SMDSEntity_Penta:
9706         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9707                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9708         break;
9709       case SMDSEntity_Hexagonal_Prism:
9710       default:
9711         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9712       }
9713       ReplaceElemInGroups(volume, NewVolume, meshDS);
9714     }
9715   }
9716
9717   if ( !theForce3d )
9718   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9719     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9720     aHelper.FixQuadraticElements(myError);
9721   }
9722 }
9723
9724 //================================================================================
9725 /*!
9726  * \brief Makes given elements quadratic
9727  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9728  *  \param theElements - elements to make quadratic
9729  */
9730 //================================================================================
9731
9732 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9733                                           TIDSortedElemSet& theElements)
9734 {
9735   if ( theElements.empty() ) return;
9736
9737   // we believe that all theElements are of the same type
9738   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9739
9740   // get all nodes shared by theElements
9741   TIDSortedNodeSet allNodes;
9742   TIDSortedElemSet::iterator eIt = theElements.begin();
9743   for ( ; eIt != theElements.end(); ++eIt )
9744     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9745
9746   // complete theElements with elements of lower dim whose all nodes are in allNodes
9747
9748   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9749   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9750   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9751   for ( ; nIt != allNodes.end(); ++nIt )
9752   {
9753     const SMDS_MeshNode* n = *nIt;
9754     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9755     while ( invIt->more() )
9756     {
9757       const SMDS_MeshElement* e = invIt->next();
9758       if ( e->IsQuadratic() )
9759       {
9760         quadAdjacentElems[ e->GetType() ].insert( e );
9761         continue;
9762       }
9763       if ( e->GetType() >= elemType )
9764       {
9765         continue; // same type of more complex linear element
9766       }
9767
9768       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9769         continue; // e is already checked
9770
9771       // check nodes
9772       bool allIn = true;
9773       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9774       while ( nodeIt->more() && allIn )
9775         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9776       if ( allIn )
9777         theElements.insert(e );
9778     }
9779   }
9780
9781   SMESH_MesherHelper helper(*myMesh);
9782   helper.SetIsQuadratic( true );
9783
9784   // add links of quadratic adjacent elements to the helper
9785
9786   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9787     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9788           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9789     {
9790       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9791     }
9792   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9793     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9794           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9795     {
9796       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9797     }
9798   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9799     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9800           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9801     {
9802       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9803     }
9804
9805   // make quadratic elements instead of linear ones
9806
9807   SMESHDS_Mesh* meshDS = GetMeshDS();
9808   SMESHDS_SubMesh* smDS = 0;
9809   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9810   {
9811     const SMDS_MeshElement* elem = *eIt;
9812     if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9813       continue;
9814
9815     const int id                   = elem->GetID();
9816     const SMDSAbs_ElementType type = elem->GetType();
9817     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9818
9819     if ( !smDS || !smDS->Contains( elem ))
9820       smDS = meshDS->MeshElements( elem->getshapeId() );
9821     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9822
9823     SMDS_MeshElement * newElem = 0;
9824     switch( nodes.size() )
9825     {
9826     case 4: // cases for most frequently used element types go first (for optimization)
9827       if ( type == SMDSAbs_Volume )
9828         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9829       else
9830         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9831       break;
9832     case 8:
9833       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9834                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9835       break;
9836     case 3:
9837       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9838       break;
9839     case 2:
9840       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9841       break;
9842     case 5:
9843       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9844                                  nodes[4], id, theForce3d);
9845       break;
9846     case 6:
9847       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9848                                  nodes[4], nodes[5], id, theForce3d);
9849       break;
9850     default:;
9851     }
9852     ReplaceElemInGroups( elem, newElem, meshDS);
9853     if( newElem && smDS )
9854       smDS->AddElement( newElem );
9855   }
9856
9857   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9858   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9859     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9860     helper.FixQuadraticElements( myError );
9861   }
9862 }
9863
9864 //=======================================================================
9865 /*!
9866  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9867  * \return int - nb of checked elements
9868  */
9869 //=======================================================================
9870
9871 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9872                                      SMDS_ElemIteratorPtr theItr,
9873                                      const int            theShapeID)
9874 {
9875   int nbElem = 0;
9876   SMESHDS_Mesh* meshDS = GetMeshDS();
9877
9878   while( theItr->more() )
9879   {
9880     const SMDS_MeshElement* elem = theItr->next();
9881     nbElem++;
9882     if( elem && elem->IsQuadratic())
9883     {
9884       int id                    = elem->GetID();
9885       int nbCornerNodes         = elem->NbCornerNodes();
9886       SMDSAbs_ElementType aType = elem->GetType();
9887
9888       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9889
9890       //remove a quadratic element
9891       if ( !theSm || !theSm->Contains( elem ))
9892         theSm = meshDS->MeshElements( elem->getshapeId() );
9893       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9894
9895       // remove medium nodes
9896       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9897         if ( nodes[i]->NbInverseElements() == 0 )
9898           meshDS->RemoveFreeNode( nodes[i], theSm );
9899
9900       // add a linear element
9901       nodes.resize( nbCornerNodes );
9902       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9903       ReplaceElemInGroups(elem, newElem, meshDS);
9904       if( theSm && newElem )
9905         theSm->AddElement( newElem );
9906     }
9907   }
9908   return nbElem;
9909 }
9910
9911 //=======================================================================
9912 //function : ConvertFromQuadratic
9913 //purpose  :
9914 //=======================================================================
9915
9916 bool SMESH_MeshEditor::ConvertFromQuadratic()
9917 {
9918   int nbCheckedElems = 0;
9919   if ( myMesh->HasShapeToMesh() )
9920   {
9921     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9922     {
9923       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9924       while ( smIt->more() ) {
9925         SMESH_subMesh* sm = smIt->next();
9926         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9927           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9928       }
9929     }
9930   }
9931
9932   int totalNbElems =
9933     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9934   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9935   {
9936     SMESHDS_SubMesh *aSM = 0;
9937     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9938   }
9939
9940   return true;
9941 }
9942
9943 namespace
9944 {
9945   //================================================================================
9946   /*!
9947    * \brief Return true if all medium nodes of the element are in the node set
9948    */
9949   //================================================================================
9950
9951   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9952   {
9953     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9954       if ( !nodeSet.count( elem->GetNode(i) ))
9955         return false;
9956     return true;
9957   }
9958 }
9959
9960 //================================================================================
9961 /*!
9962  * \brief Makes given elements linear
9963  */
9964 //================================================================================
9965
9966 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9967 {
9968   if ( theElements.empty() ) return;
9969
9970   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9971   set<int> mediumNodeIDs;
9972   TIDSortedElemSet::iterator eIt = theElements.begin();
9973   for ( ; eIt != theElements.end(); ++eIt )
9974   {
9975     const SMDS_MeshElement* e = *eIt;
9976     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9977       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9978   }
9979
9980   // replace given elements by linear ones
9981   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9982   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9983   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9984
9985   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9986   // except those elements sharing medium nodes of quadratic element whose medium nodes
9987   // are not all in mediumNodeIDs
9988
9989   // get remaining medium nodes
9990   TIDSortedNodeSet mediumNodes;
9991   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9992   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9993     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9994       mediumNodes.insert( mediumNodes.end(), n );
9995
9996   // find more quadratic elements to convert
9997   TIDSortedElemSet moreElemsToConvert;
9998   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9999   for ( ; nIt != mediumNodes.end(); ++nIt )
10000   {
10001     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10002     while ( invIt->more() )
10003     {
10004       const SMDS_MeshElement* e = invIt->next();
10005       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10006       {
10007         // find a more complex element including e and
10008         // whose medium nodes are not in mediumNodes
10009         bool complexFound = false;
10010         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10011         {
10012           SMDS_ElemIteratorPtr invIt2 =
10013             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10014           while ( invIt2->more() )
10015           {
10016             const SMDS_MeshElement* eComplex = invIt2->next();
10017             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10018             {
10019               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10020               if ( nbCommonNodes == e->NbNodes())
10021               {
10022                 complexFound = true;
10023                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10024                 break;
10025               }
10026             }
10027           }
10028         }
10029         if ( !complexFound )
10030           moreElemsToConvert.insert( e );
10031       }
10032     }
10033   }
10034   elemIt = SMDS_ElemIteratorPtr
10035     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10036   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10037 }
10038
10039 //=======================================================================
10040 //function : SewSideElements
10041 //purpose  :
10042 //=======================================================================
10043
10044 SMESH_MeshEditor::Sew_Error
10045 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10046                                    TIDSortedElemSet&    theSide2,
10047                                    const SMDS_MeshNode* theFirstNode1,
10048                                    const SMDS_MeshNode* theFirstNode2,
10049                                    const SMDS_MeshNode* theSecondNode1,
10050                                    const SMDS_MeshNode* theSecondNode2)
10051 {
10052   myLastCreatedElems.Clear();
10053   myLastCreatedNodes.Clear();
10054
10055   MESSAGE ("::::SewSideElements()");
10056   if ( theSide1.size() != theSide2.size() )
10057     return SEW_DIFF_NB_OF_ELEMENTS;
10058
10059   Sew_Error aResult = SEW_OK;
10060   // Algo:
10061   // 1. Build set of faces representing each side
10062   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10063   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10064
10065   // =======================================================================
10066   // 1. Build set of faces representing each side:
10067   // =======================================================================
10068   // a. build set of nodes belonging to faces
10069   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10070   // c. create temporary faces representing side of volumes if correspondent
10071   //    face does not exist
10072
10073   SMESHDS_Mesh* aMesh = GetMeshDS();
10074   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10075   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10076   TIDSortedElemSet             faceSet1, faceSet2;
10077   set<const SMDS_MeshElement*> volSet1,  volSet2;
10078   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10079   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10080   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10081   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10082   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10083   int iSide, iFace, iNode;
10084
10085   list<const SMDS_MeshElement* > tempFaceList;
10086   for ( iSide = 0; iSide < 2; iSide++ ) {
10087     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10088     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10089     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10090     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10091     set<const SMDS_MeshElement*>::iterator vIt;
10092     TIDSortedElemSet::iterator eIt;
10093     set<const SMDS_MeshNode*>::iterator    nIt;
10094
10095     // check that given nodes belong to given elements
10096     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10097     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10098     int firstIndex = -1, secondIndex = -1;
10099     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10100       const SMDS_MeshElement* elem = *eIt;
10101       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10102       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10103       if ( firstIndex > -1 && secondIndex > -1 ) break;
10104     }
10105     if ( firstIndex < 0 || secondIndex < 0 ) {
10106       // we can simply return until temporary faces created
10107       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10108     }
10109
10110     // -----------------------------------------------------------
10111     // 1a. Collect nodes of existing faces
10112     //     and build set of face nodes in order to detect missing
10113     //     faces corresponding to sides of volumes
10114     // -----------------------------------------------------------
10115
10116     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10117
10118     // loop on the given element of a side
10119     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10120       //const SMDS_MeshElement* elem = *eIt;
10121       const SMDS_MeshElement* elem = *eIt;
10122       if ( elem->GetType() == SMDSAbs_Face ) {
10123         faceSet->insert( elem );
10124         set <const SMDS_MeshNode*> faceNodeSet;
10125         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10126         while ( nodeIt->more() ) {
10127           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10128           nodeSet->insert( n );
10129           faceNodeSet.insert( n );
10130         }
10131         setOfFaceNodeSet.insert( faceNodeSet );
10132       }
10133       else if ( elem->GetType() == SMDSAbs_Volume )
10134         volSet->insert( elem );
10135     }
10136     // ------------------------------------------------------------------------------
10137     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10138     // ------------------------------------------------------------------------------
10139
10140     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10141       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10142       while ( fIt->more() ) { // loop on faces sharing a node
10143         const SMDS_MeshElement* f = fIt->next();
10144         if ( faceSet->find( f ) == faceSet->end() ) {
10145           // check if all nodes are in nodeSet and
10146           // complete setOfFaceNodeSet if they are
10147           set <const SMDS_MeshNode*> faceNodeSet;
10148           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10149           bool allInSet = true;
10150           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10151             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10152             if ( nodeSet->find( n ) == nodeSet->end() )
10153               allInSet = false;
10154             else
10155               faceNodeSet.insert( n );
10156           }
10157           if ( allInSet ) {
10158             faceSet->insert( f );
10159             setOfFaceNodeSet.insert( faceNodeSet );
10160           }
10161         }
10162       }
10163     }
10164
10165     // -------------------------------------------------------------------------
10166     // 1c. Create temporary faces representing sides of volumes if correspondent
10167     //     face does not exist
10168     // -------------------------------------------------------------------------
10169
10170     if ( !volSet->empty() ) {
10171       //int nodeSetSize = nodeSet->size();
10172
10173       // loop on given volumes
10174       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10175         SMDS_VolumeTool vol (*vIt);
10176         // loop on volume faces: find free faces
10177         // --------------------------------------
10178         list<const SMDS_MeshElement* > freeFaceList;
10179         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10180           if ( !vol.IsFreeFace( iFace ))
10181             continue;
10182           // check if there is already a face with same nodes in a face set
10183           const SMDS_MeshElement* aFreeFace = 0;
10184           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10185           int nbNodes = vol.NbFaceNodes( iFace );
10186           set <const SMDS_MeshNode*> faceNodeSet;
10187           vol.GetFaceNodes( iFace, faceNodeSet );
10188           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10189           if ( isNewFace ) {
10190             // no such a face is given but it still can exist, check it
10191             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10192             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10193           }
10194           if ( !aFreeFace ) {
10195             // create a temporary face
10196             if ( nbNodes == 3 ) {
10197               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10198               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10199             }
10200             else if ( nbNodes == 4 ) {
10201               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10202               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10203             }
10204             else {
10205               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10206               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10207               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10208             }
10209             if ( aFreeFace )
10210               tempFaceList.push_back( aFreeFace );
10211           }
10212
10213           if ( aFreeFace )
10214             freeFaceList.push_back( aFreeFace );
10215
10216         } // loop on faces of a volume
10217
10218         // choose one of several free faces of a volume
10219         // --------------------------------------------
10220         if ( freeFaceList.size() > 1 ) {
10221           // choose a face having max nb of nodes shared by other elems of a side
10222           int maxNbNodes = -1;
10223           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10224           while ( fIt != freeFaceList.end() ) { // loop on free faces
10225             int nbSharedNodes = 0;
10226             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10227             while ( nodeIt->more() ) { // loop on free face nodes
10228               const SMDS_MeshNode* n =
10229                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10230               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10231               while ( invElemIt->more() ) {
10232                 const SMDS_MeshElement* e = invElemIt->next();
10233                 nbSharedNodes += faceSet->count( e );
10234                 nbSharedNodes += elemSet->count( e );
10235               }
10236             }
10237             if ( nbSharedNodes > maxNbNodes ) {
10238               maxNbNodes = nbSharedNodes;
10239               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10240             }
10241             else if ( nbSharedNodes == maxNbNodes ) {
10242               fIt++;
10243             }
10244             else {
10245               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10246             }
10247           }
10248           if ( freeFaceList.size() > 1 )
10249           {
10250             // could not choose one face, use another way
10251             // choose a face most close to the bary center of the opposite side
10252             gp_XYZ aBC( 0., 0., 0. );
10253             set <const SMDS_MeshNode*> addedNodes;
10254             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10255             eIt = elemSet2->begin();
10256             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10257               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10258               while ( nodeIt->more() ) { // loop on free face nodes
10259                 const SMDS_MeshNode* n =
10260                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10261                 if ( addedNodes.insert( n ).second )
10262                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10263               }
10264             }
10265             aBC /= addedNodes.size();
10266             double minDist = DBL_MAX;
10267             fIt = freeFaceList.begin();
10268             while ( fIt != freeFaceList.end() ) { // loop on free faces
10269               double dist = 0;
10270               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10271               while ( nodeIt->more() ) { // loop on free face nodes
10272                 const SMDS_MeshNode* n =
10273                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10274                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10275                 dist += ( aBC - p ).SquareModulus();
10276               }
10277               if ( dist < minDist ) {
10278                 minDist = dist;
10279                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10280               }
10281               else
10282                 fIt = freeFaceList.erase( fIt++ );
10283             }
10284           }
10285         } // choose one of several free faces of a volume
10286
10287         if ( freeFaceList.size() == 1 ) {
10288           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10289           faceSet->insert( aFreeFace );
10290           // complete a node set with nodes of a found free face
10291           //           for ( iNode = 0; iNode < ; iNode++ )
10292           //             nodeSet->insert( fNodes[ iNode ] );
10293         }
10294
10295       } // loop on volumes of a side
10296
10297       //       // complete a set of faces if new nodes in a nodeSet appeared
10298       //       // ----------------------------------------------------------
10299       //       if ( nodeSetSize != nodeSet->size() ) {
10300       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10301       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10302       //           while ( fIt->more() ) { // loop on faces sharing a node
10303       //             const SMDS_MeshElement* f = fIt->next();
10304       //             if ( faceSet->find( f ) == faceSet->end() ) {
10305       //               // check if all nodes are in nodeSet and
10306       //               // complete setOfFaceNodeSet if they are
10307       //               set <const SMDS_MeshNode*> faceNodeSet;
10308       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10309       //               bool allInSet = true;
10310       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10311       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10312       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10313       //                   allInSet = false;
10314       //                 else
10315       //                   faceNodeSet.insert( n );
10316       //               }
10317       //               if ( allInSet ) {
10318       //                 faceSet->insert( f );
10319       //                 setOfFaceNodeSet.insert( faceNodeSet );
10320       //               }
10321       //             }
10322       //           }
10323       //         }
10324       //       }
10325     } // Create temporary faces, if there are volumes given
10326   } // loop on sides
10327
10328   if ( faceSet1.size() != faceSet2.size() ) {
10329     // delete temporary faces: they are in reverseElements of actual nodes
10330 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10331 //    while ( tmpFaceIt->more() )
10332 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10333 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10334 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10335 //      aMesh->RemoveElement(*tmpFaceIt);
10336     MESSAGE("Diff nb of faces");
10337     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10338   }
10339
10340   // ============================================================
10341   // 2. Find nodes to merge:
10342   //              bind a node to remove to a node to put instead
10343   // ============================================================
10344
10345   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10346   if ( theFirstNode1 != theFirstNode2 )
10347     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10348   if ( theSecondNode1 != theSecondNode2 )
10349     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10350
10351   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10352   set< long > linkIdSet; // links to process
10353   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10354
10355   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10356   list< NLink > linkList[2];
10357   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10358   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10359   // loop on links in linkList; find faces by links and append links
10360   // of the found faces to linkList
10361   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10362   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10363   {
10364     NLink link[] = { *linkIt[0], *linkIt[1] };
10365     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10366     if ( !linkIdSet.count( linkID ) )
10367       continue;
10368
10369     // by links, find faces in the face sets,
10370     // and find indices of link nodes in the found faces;
10371     // in a face set, there is only one or no face sharing a link
10372     // ---------------------------------------------------------------
10373
10374     const SMDS_MeshElement* face[] = { 0, 0 };
10375     vector<const SMDS_MeshNode*> fnodes[2];
10376     int iLinkNode[2][2];
10377     TIDSortedElemSet avoidSet;
10378     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10379       const SMDS_MeshNode* n1 = link[iSide].first;
10380       const SMDS_MeshNode* n2 = link[iSide].second;
10381       //cout << "Side " << iSide << " ";
10382       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10383       // find a face by two link nodes
10384       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10385                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10386       if ( face[ iSide ])
10387       {
10388         //cout << " F " << face[ iSide]->GetID() <<endl;
10389         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10390         // put face nodes to fnodes
10391         if ( face[ iSide ]->IsQuadratic() )
10392         {
10393           // use interlaced nodes iterator
10394           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10395           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10396           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10397           while ( nIter->more() )
10398             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10399         }
10400         else
10401         {
10402           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10403                                   face[ iSide ]->end_nodes() );
10404         }
10405         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10406       }
10407     }
10408
10409     // check similarity of elements of the sides
10410     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10411       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10412       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10413         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10414       }
10415       else {
10416         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10417       }
10418       break; // do not return because it's necessary to remove tmp faces
10419     }
10420
10421     // set nodes to merge
10422     // -------------------
10423
10424     if ( face[0] && face[1] )  {
10425       const int nbNodes = face[0]->NbNodes();
10426       if ( nbNodes != face[1]->NbNodes() ) {
10427         MESSAGE("Diff nb of face nodes");
10428         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10429         break; // do not return because it s necessary to remove tmp faces
10430       }
10431       bool reverse[] = { false, false }; // order of nodes in the link
10432       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10433         // analyse link orientation in faces
10434         int i1 = iLinkNode[ iSide ][ 0 ];
10435         int i2 = iLinkNode[ iSide ][ 1 ];
10436         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10437       }
10438       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10439       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10440       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10441       {
10442         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10443                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10444       }
10445
10446       // add other links of the faces to linkList
10447       // -----------------------------------------
10448
10449       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10450         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10451         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10452         if ( !iter_isnew.second ) { // already in a set: no need to process
10453           linkIdSet.erase( iter_isnew.first );
10454         }
10455         else // new in set == encountered for the first time: add
10456         {
10457           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10458           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10459           linkList[0].push_back ( NLink( n1, n2 ));
10460           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10461         }
10462       }
10463     } // 2 faces found
10464
10465     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10466       break;
10467
10468   } // loop on link lists
10469
10470   if ( aResult == SEW_OK &&
10471        ( //linkIt[0] != linkList[0].end() ||
10472          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10473     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10474              " " << (faceSetPtr[1]->empty()));
10475     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10476   }
10477
10478   // ====================================================================
10479   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10480   // ====================================================================
10481
10482   // delete temporary faces
10483 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10484 //  while ( tmpFaceIt->more() )
10485 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10486   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10487   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10488     aMesh->RemoveElement(*tmpFaceIt);
10489
10490   if ( aResult != SEW_OK)
10491     return aResult;
10492
10493   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10494   // loop on nodes replacement map
10495   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10496   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10497     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10498       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10499       nodeIDsToRemove.push_back( nToRemove->GetID() );
10500       // loop on elements sharing nToRemove
10501       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10502       while ( invElemIt->more() ) {
10503         const SMDS_MeshElement* e = invElemIt->next();
10504         // get a new suite of nodes: make replacement
10505         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10506         vector< const SMDS_MeshNode*> nodes( nbNodes );
10507         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10508         while ( nIt->more() ) {
10509           const SMDS_MeshNode* n =
10510             static_cast<const SMDS_MeshNode*>( nIt->next() );
10511           nnIt = nReplaceMap.find( n );
10512           if ( nnIt != nReplaceMap.end() ) {
10513             nbReplaced++;
10514             n = (*nnIt).second;
10515           }
10516           nodes[ i++ ] = n;
10517         }
10518         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10519         //         elemIDsToRemove.push_back( e->GetID() );
10520         //       else
10521         if ( nbReplaced )
10522           {
10523             SMDSAbs_ElementType etyp = e->GetType();
10524             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10525             if (newElem)
10526               {
10527                 myLastCreatedElems.Append(newElem);
10528                 AddToSameGroups(newElem, e, aMesh);
10529                 int aShapeId = e->getshapeId();
10530                 if ( aShapeId )
10531                   {
10532                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10533                   }
10534               }
10535             aMesh->RemoveElement(e);
10536           }
10537       }
10538     }
10539
10540   Remove( nodeIDsToRemove, true );
10541
10542   return aResult;
10543 }
10544
10545 //================================================================================
10546 /*!
10547  * \brief Find corresponding nodes in two sets of faces
10548  * \param theSide1 - first face set
10549  * \param theSide2 - second first face
10550  * \param theFirstNode1 - a boundary node of set 1
10551  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10552  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10553  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10554  * \param nReplaceMap - output map of corresponding nodes
10555  * \return bool  - is a success or not
10556  */
10557 //================================================================================
10558
10559 #ifdef _DEBUG_
10560 //#define DEBUG_MATCHING_NODES
10561 #endif
10562
10563 SMESH_MeshEditor::Sew_Error
10564 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10565                                     set<const SMDS_MeshElement*>& theSide2,
10566                                     const SMDS_MeshNode*          theFirstNode1,
10567                                     const SMDS_MeshNode*          theFirstNode2,
10568                                     const SMDS_MeshNode*          theSecondNode1,
10569                                     const SMDS_MeshNode*          theSecondNode2,
10570                                     TNodeNodeMap &                nReplaceMap)
10571 {
10572   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10573
10574   nReplaceMap.clear();
10575   if ( theFirstNode1 != theFirstNode2 )
10576     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10577   if ( theSecondNode1 != theSecondNode2 )
10578     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10579
10580   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10581   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10582
10583   list< NLink > linkList[2];
10584   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10585   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10586
10587   // loop on links in linkList; find faces by links and append links
10588   // of the found faces to linkList
10589   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10590   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10591     NLink link[] = { *linkIt[0], *linkIt[1] };
10592     if ( linkSet.find( link[0] ) == linkSet.end() )
10593       continue;
10594
10595     // by links, find faces in the face sets,
10596     // and find indices of link nodes in the found faces;
10597     // in a face set, there is only one or no face sharing a link
10598     // ---------------------------------------------------------------
10599
10600     const SMDS_MeshElement* face[] = { 0, 0 };
10601     list<const SMDS_MeshNode*> notLinkNodes[2];
10602     //bool reverse[] = { false, false }; // order of notLinkNodes
10603     int nbNodes[2];
10604     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10605     {
10606       const SMDS_MeshNode* n1 = link[iSide].first;
10607       const SMDS_MeshNode* n2 = link[iSide].second;
10608       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10609       set< const SMDS_MeshElement* > facesOfNode1;
10610       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10611       {
10612         // during a loop of the first node, we find all faces around n1,
10613         // during a loop of the second node, we find one face sharing both n1 and n2
10614         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10615         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10616         while ( fIt->more() ) { // loop on faces sharing a node
10617           const SMDS_MeshElement* f = fIt->next();
10618           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10619               ! facesOfNode1.insert( f ).second ) // f encounters twice
10620           {
10621             if ( face[ iSide ] ) {
10622               MESSAGE( "2 faces per link " );
10623               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10624             }
10625             face[ iSide ] = f;
10626             faceSet->erase( f );
10627
10628             // get not link nodes
10629             int nbN = f->NbNodes();
10630             if ( f->IsQuadratic() )
10631               nbN /= 2;
10632             nbNodes[ iSide ] = nbN;
10633             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10634             int i1 = f->GetNodeIndex( n1 );
10635             int i2 = f->GetNodeIndex( n2 );
10636             int iEnd = nbN, iBeg = -1, iDelta = 1;
10637             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10638             if ( reverse ) {
10639               std::swap( iEnd, iBeg ); iDelta = -1;
10640             }
10641             int i = i2;
10642             while ( true ) {
10643               i += iDelta;
10644               if ( i == iEnd ) i = iBeg + iDelta;
10645               if ( i == i1 ) break;
10646               nodes.push_back ( f->GetNode( i ) );
10647             }
10648           }
10649         }
10650       }
10651     }
10652     // check similarity of elements of the sides
10653     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10654       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10655       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10656         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10657       }
10658       else {
10659         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10660       }
10661     }
10662
10663     // set nodes to merge
10664     // -------------------
10665
10666     if ( face[0] && face[1] )  {
10667       if ( nbNodes[0] != nbNodes[1] ) {
10668         MESSAGE("Diff nb of face nodes");
10669         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10670       }
10671 #ifdef DEBUG_MATCHING_NODES
10672       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10673                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10674                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10675 #endif
10676       int nbN = nbNodes[0];
10677       {
10678         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10679         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10680         for ( int i = 0 ; i < nbN - 2; ++i ) {
10681 #ifdef DEBUG_MATCHING_NODES
10682           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10683 #endif
10684           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10685         }
10686       }
10687
10688       // add other links of the face 1 to linkList
10689       // -----------------------------------------
10690
10691       const SMDS_MeshElement* f0 = face[0];
10692       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10693       for ( int i = 0; i < nbN; i++ )
10694       {
10695         const SMDS_MeshNode* n2 = f0->GetNode( i );
10696         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10697           linkSet.insert( SMESH_TLink( n1, n2 ));
10698         if ( !iter_isnew.second ) { // already in a set: no need to process
10699           linkSet.erase( iter_isnew.first );
10700         }
10701         else // new in set == encountered for the first time: add
10702         {
10703 #ifdef DEBUG_MATCHING_NODES
10704           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10705                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10706 #endif
10707           linkList[0].push_back ( NLink( n1, n2 ));
10708           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10709         }
10710         n1 = n2;
10711       }
10712     } // 2 faces found
10713   } // loop on link lists
10714
10715   return SEW_OK;
10716 }
10717
10718 //================================================================================
10719 /*!
10720   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10721   \param theElems - the list of elements (edges or faces) to be replicated
10722   The nodes for duplication could be found from these elements
10723   \param theNodesNot - list of nodes to NOT replicate
10724   \param theAffectedElems - the list of elements (cells and edges) to which the
10725   replicated nodes should be associated to.
10726   \return TRUE if operation has been completed successfully, FALSE otherwise
10727 */
10728 //================================================================================
10729
10730 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10731                                     const TIDSortedElemSet& theNodesNot,
10732                                     const TIDSortedElemSet& theAffectedElems )
10733 {
10734   myLastCreatedElems.Clear();
10735   myLastCreatedNodes.Clear();
10736
10737   if ( theElems.size() == 0 )
10738     return false;
10739
10740   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10741   if ( !aMeshDS )
10742     return false;
10743
10744   bool res = false;
10745   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10746   // duplicate elements and nodes
10747   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10748   // replce nodes by duplications
10749   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10750   return res;
10751 }
10752
10753 //================================================================================
10754 /*!
10755   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10756   \param theMeshDS - mesh instance
10757   \param theElems - the elements replicated or modified (nodes should be changed)
10758   \param theNodesNot - nodes to NOT replicate
10759   \param theNodeNodeMap - relation of old node to new created node
10760   \param theIsDoubleElem - flag os to replicate element or modify
10761   \return TRUE if operation has been completed successfully, FALSE otherwise
10762 */
10763 //================================================================================
10764
10765 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10766                                     const TIDSortedElemSet& theElems,
10767                                     const TIDSortedElemSet& theNodesNot,
10768                                     std::map< const SMDS_MeshNode*,
10769                                     const SMDS_MeshNode* >& theNodeNodeMap,
10770                                     const bool theIsDoubleElem )
10771 {
10772   MESSAGE("doubleNodes");
10773   // iterate on through element and duplicate them (by nodes duplication)
10774   bool res = false;
10775   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10776   for ( ;  elemItr != theElems.end(); ++elemItr )
10777   {
10778     const SMDS_MeshElement* anElem = *elemItr;
10779     if (!anElem)
10780       continue;
10781
10782     bool isDuplicate = false;
10783     // duplicate nodes to duplicate element
10784     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10785     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10786     int ind = 0;
10787     while ( anIter->more() )
10788     {
10789
10790       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10791       SMDS_MeshNode* aNewNode = aCurrNode;
10792       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10793         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10794       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10795       {
10796         // duplicate node
10797         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10798         theNodeNodeMap[ aCurrNode ] = aNewNode;
10799         myLastCreatedNodes.Append( aNewNode );
10800       }
10801       isDuplicate |= (aCurrNode != aNewNode);
10802       newNodes[ ind++ ] = aNewNode;
10803     }
10804     if ( !isDuplicate )
10805       continue;
10806
10807     if ( theIsDoubleElem )
10808       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10809     else
10810       {
10811       MESSAGE("ChangeElementNodes");
10812       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10813       }
10814     res = true;
10815   }
10816   return res;
10817 }
10818
10819 //================================================================================
10820 /*!
10821   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10822   \param theNodes - identifiers of nodes to be doubled
10823   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10824          nodes. If list of element identifiers is empty then nodes are doubled but
10825          they not assigned to elements
10826   \return TRUE if operation has been completed successfully, FALSE otherwise
10827 */
10828 //================================================================================
10829
10830 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10831                                     const std::list< int >& theListOfModifiedElems )
10832 {
10833   MESSAGE("DoubleNodes");
10834   myLastCreatedElems.Clear();
10835   myLastCreatedNodes.Clear();
10836
10837   if ( theListOfNodes.size() == 0 )
10838     return false;
10839
10840   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10841   if ( !aMeshDS )
10842     return false;
10843
10844   // iterate through nodes and duplicate them
10845
10846   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10847
10848   std::list< int >::const_iterator aNodeIter;
10849   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10850   {
10851     int aCurr = *aNodeIter;
10852     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10853     if ( !aNode )
10854       continue;
10855
10856     // duplicate node
10857
10858     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10859     if ( aNewNode )
10860     {
10861       anOldNodeToNewNode[ aNode ] = aNewNode;
10862       myLastCreatedNodes.Append( aNewNode );
10863     }
10864   }
10865
10866   // Create map of new nodes for modified elements
10867
10868   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10869
10870   std::list< int >::const_iterator anElemIter;
10871   for ( anElemIter = theListOfModifiedElems.begin();
10872         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10873   {
10874     int aCurr = *anElemIter;
10875     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10876     if ( !anElem )
10877       continue;
10878
10879     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10880
10881     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10882     int ind = 0;
10883     while ( anIter->more() )
10884     {
10885       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10886       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10887       {
10888         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10889         aNodeArr[ ind++ ] = aNewNode;
10890       }
10891       else
10892         aNodeArr[ ind++ ] = aCurrNode;
10893     }
10894     anElemToNodes[ anElem ] = aNodeArr;
10895   }
10896
10897   // Change nodes of elements
10898
10899   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10900     anElemToNodesIter = anElemToNodes.begin();
10901   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10902   {
10903     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10904     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10905     if ( anElem )
10906       {
10907       MESSAGE("ChangeElementNodes");
10908       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10909       }
10910   }
10911
10912   return true;
10913 }
10914
10915 namespace {
10916
10917   //================================================================================
10918   /*!
10919   \brief Check if element located inside shape
10920   \return TRUE if IN or ON shape, FALSE otherwise
10921   */
10922   //================================================================================
10923
10924   template<class Classifier>
10925   bool isInside(const SMDS_MeshElement* theElem,
10926                 Classifier&             theClassifier,
10927                 const double            theTol)
10928   {
10929     gp_XYZ centerXYZ (0, 0, 0);
10930     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10931     while (aNodeItr->more())
10932       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10933
10934     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10935     theClassifier.Perform(aPnt, theTol);
10936     TopAbs_State aState = theClassifier.State();
10937     return (aState == TopAbs_IN || aState == TopAbs_ON );
10938   }
10939
10940   //================================================================================
10941   /*!
10942    * \brief Classifier of the 3D point on the TopoDS_Face
10943    *        with interaface suitable for isInside()
10944    */
10945   //================================================================================
10946
10947   struct _FaceClassifier
10948   {
10949     Extrema_ExtPS       _extremum;
10950     BRepAdaptor_Surface _surface;
10951     TopAbs_State        _state;
10952
10953     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10954     {
10955       _extremum.Initialize( _surface,
10956                             _surface.FirstUParameter(), _surface.LastUParameter(),
10957                             _surface.FirstVParameter(), _surface.LastVParameter(),
10958                             _surface.Tolerance(), _surface.Tolerance() );
10959     }
10960     void Perform(const gp_Pnt& aPnt, double theTol)
10961     {
10962       _state = TopAbs_OUT;
10963       _extremum.Perform(aPnt);
10964       if ( _extremum.IsDone() )
10965         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10966 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10967           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10968 #else
10969           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10970 #endif
10971     }
10972     TopAbs_State State() const
10973     {
10974       return _state;
10975     }
10976   };
10977 }
10978
10979 //================================================================================
10980 /*!
10981   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10982   This method is the first step of DoubleNodeElemGroupsInRegion.
10983   \param theElems - list of groups of elements (edges or faces) to be replicated
10984   \param theNodesNot - list of groups of nodes not to replicated
10985   \param theShape - shape to detect affected elements (element which geometric center
10986          located on or inside shape).
10987          The replicated nodes should be associated to affected elements.
10988   \return groups of affected elements
10989   \sa DoubleNodeElemGroupsInRegion()
10990  */
10991 //================================================================================
10992
10993 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10994                                                    const TIDSortedElemSet& theNodesNot,
10995                                                    const TopoDS_Shape&     theShape,
10996                                                    TIDSortedElemSet&       theAffectedElems)
10997 {
10998   if ( theShape.IsNull() )
10999     return false;
11000
11001   const double aTol = Precision::Confusion();
11002   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11003   auto_ptr<_FaceClassifier>              aFaceClassifier;
11004   if ( theShape.ShapeType() == TopAbs_SOLID )
11005   {
11006     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11007     bsc3d->PerformInfinitePoint(aTol);
11008   }
11009   else if (theShape.ShapeType() == TopAbs_FACE )
11010   {
11011     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11012   }
11013
11014   // iterates on indicated elements and get elements by back references from their nodes
11015   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11016   for ( ;  elemItr != theElems.end(); ++elemItr )
11017   {
11018     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11019     if (!anElem)
11020       continue;
11021
11022     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11023     while ( nodeItr->more() )
11024     {
11025       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11026       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11027         continue;
11028       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11029       while ( backElemItr->more() )
11030       {
11031         const SMDS_MeshElement* curElem = backElemItr->next();
11032         if ( curElem && theElems.find(curElem) == theElems.end() &&
11033              ( bsc3d.get() ?
11034                isInside( curElem, *bsc3d, aTol ) :
11035                isInside( curElem, *aFaceClassifier, aTol )))
11036           theAffectedElems.insert( curElem );
11037       }
11038     }
11039   }
11040   return true;
11041 }
11042
11043 //================================================================================
11044 /*!
11045   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11046   \param theElems - group of of elements (edges or faces) to be replicated
11047   \param theNodesNot - group of nodes not to replicate
11048   \param theShape - shape to detect affected elements (element which geometric center
11049   located on or inside shape).
11050   The replicated nodes should be associated to affected elements.
11051   \return TRUE if operation has been completed successfully, FALSE otherwise
11052 */
11053 //================================================================================
11054
11055 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11056                                             const TIDSortedElemSet& theNodesNot,
11057                                             const TopoDS_Shape&     theShape )
11058 {
11059   if ( theShape.IsNull() )
11060     return false;
11061
11062   const double aTol = Precision::Confusion();
11063   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11064   auto_ptr<_FaceClassifier>              aFaceClassifier;
11065   if ( theShape.ShapeType() == TopAbs_SOLID )
11066   {
11067     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11068     bsc3d->PerformInfinitePoint(aTol);
11069   }
11070   else if (theShape.ShapeType() == TopAbs_FACE )
11071   {
11072     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11073   }
11074
11075   // iterates on indicated elements and get elements by back references from their nodes
11076   TIDSortedElemSet anAffected;
11077   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11078   for ( ;  elemItr != theElems.end(); ++elemItr )
11079   {
11080     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11081     if (!anElem)
11082       continue;
11083
11084     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11085     while ( nodeItr->more() )
11086     {
11087       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11088       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11089         continue;
11090       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11091       while ( backElemItr->more() )
11092       {
11093         const SMDS_MeshElement* curElem = backElemItr->next();
11094         if ( curElem && theElems.find(curElem) == theElems.end() &&
11095              ( bsc3d.get() ?
11096                isInside( curElem, *bsc3d, aTol ) :
11097                isInside( curElem, *aFaceClassifier, aTol )))
11098           anAffected.insert( curElem );
11099       }
11100     }
11101   }
11102   return DoubleNodes( theElems, theNodesNot, anAffected );
11103 }
11104
11105 /*!
11106  *  \brief compute an oriented angle between two planes defined by four points.
11107  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11108  *  @param p0 base of the rotation axe
11109  *  @param p1 extremity of the rotation axe
11110  *  @param g1 belongs to the first plane
11111  *  @param g2 belongs to the second plane
11112  */
11113 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11114 {
11115 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11116 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11117 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11118 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11119   gp_Vec vref(p0, p1);
11120   gp_Vec v1(p0, g1);
11121   gp_Vec v2(p0, g2);
11122   gp_Vec n1 = vref.Crossed(v1);
11123   gp_Vec n2 = vref.Crossed(v2);
11124   return n2.AngleWithRef(n1, vref);
11125 }
11126
11127 /*!
11128  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11129  * The list of groups must describe a partition of the mesh volumes.
11130  * The nodes of the internal faces at the boundaries of the groups are doubled.
11131  * In option, the internal faces are replaced by flat elements.
11132  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11133  * The flat elements are stored in groups of volumes.
11134  * @param theElems - list of groups of volumes, where a group of volume is a set of
11135  * SMDS_MeshElements sorted by Id.
11136  * @param createJointElems - if TRUE, create the elements
11137  * @return TRUE if operation has been completed successfully, FALSE otherwise
11138  */
11139 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11140                                                      bool createJointElems)
11141 {
11142   MESSAGE("----------------------------------------------");
11143   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11144   MESSAGE("----------------------------------------------");
11145
11146   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11147   meshDS->BuildDownWardConnectivity(true);
11148   CHRONO(50);
11149   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11150
11151   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11152   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11153   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11154
11155   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11156   std::map<int,int>celldom; // cell vtkId --> domain
11157   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11158   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11159   faceDomains.clear();
11160   celldom.clear();
11161   cellDomains.clear();
11162   nodeDomains.clear();
11163   std::map<int,int> emptyMap;
11164   std::set<int> emptySet;
11165   emptyMap.clear();
11166
11167   for (int idom = 0; idom < theElems.size(); idom++)
11168     {
11169
11170       // --- build a map (face to duplicate --> volume to modify)
11171       //     with all the faces shared by 2 domains (group of elements)
11172       //     and corresponding volume of this domain, for each shared face.
11173       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11174
11175       //MESSAGE("Domain " << idom);
11176       const TIDSortedElemSet& domain = theElems[idom];
11177       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11178       for (; elemItr != domain.end(); ++elemItr)
11179         {
11180           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11181           if (!anElem)
11182             continue;
11183           int vtkId = anElem->getVtkId();
11184           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11185           int neighborsVtkIds[NBMAXNEIGHBORS];
11186           int downIds[NBMAXNEIGHBORS];
11187           unsigned char downTypes[NBMAXNEIGHBORS];
11188           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11189           for (int n = 0; n < nbNeighbors; n++)
11190             {
11191               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11192               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11193               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11194                 {
11195                   DownIdType face(downIds[n], downTypes[n]);
11196                   if (!faceDomains.count(face))
11197                     faceDomains[face] = emptyMap; // create an empty entry for face
11198                   if (!faceDomains[face].count(idom))
11199                     {
11200                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11201                       celldom[vtkId] = idom;
11202                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11203                     }
11204                 }
11205             }
11206         }
11207     }
11208
11209   //MESSAGE("Number of shared faces " << faceDomains.size());
11210   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11211
11212   // --- explore the shared faces domain by domain,
11213   //     explore the nodes of the face and see if they belong to a cell in the domain,
11214   //     which has only a node or an edge on the border (not a shared face)
11215
11216   for (int idomain = 0; idomain < theElems.size(); idomain++)
11217     {
11218       //MESSAGE("Domain " << idomain);
11219       const TIDSortedElemSet& domain = theElems[idomain];
11220       itface = faceDomains.begin();
11221       for (; itface != faceDomains.end(); ++itface)
11222         {
11223           std::map<int, int> domvol = itface->second;
11224           if (!domvol.count(idomain))
11225             continue;
11226           DownIdType face = itface->first;
11227           //MESSAGE(" --- face " << face.cellId);
11228           std::set<int> oldNodes;
11229           oldNodes.clear();
11230           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11231           std::set<int>::iterator itn = oldNodes.begin();
11232           for (; itn != oldNodes.end(); ++itn)
11233             {
11234               int oldId = *itn;
11235               //MESSAGE("     node " << oldId);
11236               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11237               for (int i=0; i<l.ncells; i++)
11238                 {
11239                   int vtkId = l.cells[i];
11240                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11241                   if (!domain.count(anElem))
11242                     continue;
11243                   int vtkType = grid->GetCellType(vtkId);
11244                   int downId = grid->CellIdToDownId(vtkId);
11245                   if (downId < 0)
11246                     {
11247                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11248                       continue; // not OK at this stage of the algorithm:
11249                                 //no cells created after BuildDownWardConnectivity
11250                     }
11251                   DownIdType aCell(downId, vtkType);
11252                   if (!cellDomains.count(aCell))
11253                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11254                   cellDomains[aCell][idomain] = vtkId;
11255                   celldom[vtkId] = idomain;
11256                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11257                 }
11258             }
11259         }
11260     }
11261
11262   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11263   //     for each shared face, get the nodes
11264   //     for each node, for each domain of the face, create a clone of the node
11265
11266   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11267   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11268   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11269
11270   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11271   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11272   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11273
11274   for (int idomain = 0; idomain < theElems.size(); idomain++)
11275     {
11276       itface = faceDomains.begin();
11277       for (; itface != faceDomains.end(); ++itface)
11278         {
11279           std::map<int, int> domvol = itface->second;
11280           if (!domvol.count(idomain))
11281             continue;
11282           DownIdType face = itface->first;
11283           //MESSAGE(" --- face " << face.cellId);
11284           std::set<int> oldNodes;
11285           oldNodes.clear();
11286           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11287           std::set<int>::iterator itn = oldNodes.begin();
11288           for (; itn != oldNodes.end(); ++itn)
11289             {
11290               int oldId = *itn;
11291               //MESSAGE("-+-+-a node " << oldId);
11292               if (!nodeDomains.count(oldId))
11293                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11294               if (nodeDomains[oldId].empty())
11295                 {
11296                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11297                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11298                 }
11299               std::map<int, int>::iterator itdom = domvol.begin();
11300               for (; itdom != domvol.end(); ++itdom)
11301                 {
11302                   int idom = itdom->first;
11303                   //MESSAGE("         domain " << idom);
11304                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11305                     {
11306                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11307                         {
11308                           vector<int> orderedDoms;
11309                           //MESSAGE("multiple node " << oldId);
11310                           if (mutipleNodes.count(oldId))
11311                             orderedDoms = mutipleNodes[oldId];
11312                           else
11313                             {
11314                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11315                               for (; it != nodeDomains[oldId].end(); ++it)
11316                                 orderedDoms.push_back(it->first);
11317                             }
11318                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11319                           //stringstream txt;
11320                           //for (int i=0; i<orderedDoms.size(); i++)
11321                           //  txt << orderedDoms[i] << " ";
11322                           //MESSAGE("orderedDoms " << txt.str());
11323                           mutipleNodes[oldId] = orderedDoms;
11324                         }
11325                       double *coords = grid->GetPoint(oldId);
11326                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11327                       int newId = newNode->getVtkId();
11328                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11329                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11330                     }
11331                 }
11332             }
11333         }
11334     }
11335
11336   for (int idomain = 0; idomain < theElems.size(); idomain++)
11337     {
11338       itface = faceDomains.begin();
11339       for (; itface != faceDomains.end(); ++itface)
11340         {
11341           std::map<int, int> domvol = itface->second;
11342           if (!domvol.count(idomain))
11343             continue;
11344           DownIdType face = itface->first;
11345           //MESSAGE(" --- face " << face.cellId);
11346           std::set<int> oldNodes;
11347           oldNodes.clear();
11348           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11349           int nbMultipleNodes = 0;
11350           std::set<int>::iterator itn = oldNodes.begin();
11351           for (; itn != oldNodes.end(); ++itn)
11352             {
11353               int oldId = *itn;
11354               if (mutipleNodes.count(oldId))
11355                 nbMultipleNodes++;
11356             }
11357           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11358             {
11359               //MESSAGE("multiple Nodes detected on a shared face");
11360               int downId = itface->first.cellId;
11361               unsigned char cellType = itface->first.cellType;
11362               // --- shared edge or shared face ?
11363               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11364                 {
11365                   int nodes[3];
11366                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11367                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11368                     if (mutipleNodes.count(nodes[i]))
11369                       if (!mutipleNodesToFace.count(nodes[i]))
11370                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11371                 }
11372               else // shared face (between two volumes)
11373                 {
11374                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11375                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11376                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11377                   for (int ie =0; ie < nbEdges; ie++)
11378                     {
11379                       int nodes[3];
11380                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11381                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11382                         {
11383                           vector<int> vn0 = mutipleNodes[nodes[0]];
11384                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11385                           vector<int> doms;
11386                           for (int i0 = 0; i0 < vn0.size(); i0++)
11387                             for (int i1 = 0; i1 < vn1.size(); i1++)
11388                               if (vn0[i0] == vn1[i1])
11389                                 doms.push_back(vn0[i0]);
11390                           if (doms.size() >2)
11391                             {
11392                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11393                               double *coords = grid->GetPoint(nodes[0]);
11394                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11395                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11396                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11397                               gp_Pnt gref;
11398                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11399                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11400                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11401                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11402                               for (int id=0; id < doms.size(); id++)
11403                                 {
11404                                   int idom = doms[id];
11405                                   for (int ivol=0; ivol<nbvol; ivol++)
11406                                     {
11407                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11408                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11409                                       if (theElems[idom].count(elem))
11410                                         {
11411                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11412                                           domvol[idom] = svol;
11413                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11414                                           double values[3];
11415                                           vtkIdType npts = 0;
11416                                           vtkIdType* pts = 0;
11417                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11418                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11419                                           if (id ==0)
11420                                             {
11421                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11422                                               angleDom[idom] = 0;
11423                                             }
11424                                           else
11425                                             {
11426                                               gp_Pnt g(values[0], values[1], values[2]);
11427                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11428                                               //MESSAGE("  angle=" << angleDom[idom]);
11429                                             }
11430                                           break;
11431                                         }
11432                                     }
11433                                 }
11434                               map<double, int> sortedDom; // sort domains by angle
11435                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11436                                 sortedDom[ia->second] = ia->first;
11437                               vector<int> vnodes;
11438                               vector<int> vdom;
11439                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11440                                 {
11441                                   vdom.push_back(ib->second);
11442                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11443                                 }
11444                               for (int ino = 0; ino < nbNodes; ino++)
11445                                 vnodes.push_back(nodes[ino]);
11446                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11447                             }
11448                         }
11449                     }
11450                 }
11451             }
11452         }
11453     }
11454
11455   // --- iterate on shared faces (volumes to modify, face to extrude)
11456   //     get node id's of the face (id SMDS = id VTK)
11457   //     create flat element with old and new nodes if requested
11458
11459   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11460   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11461
11462   std::map<int, std::map<long,int> > nodeQuadDomains;
11463   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11464
11465   if (createJointElems)
11466     {
11467       int idg;
11468       string joints2DName = "joints2D";
11469       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11470       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11471       string joints3DName = "joints3D";
11472       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11473       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11474
11475       itface = faceDomains.begin();
11476       for (; itface != faceDomains.end(); ++itface)
11477         {
11478           DownIdType face = itface->first;
11479           std::set<int> oldNodes;
11480           std::set<int>::iterator itn;
11481           oldNodes.clear();
11482           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11483
11484           std::map<int, int> domvol = itface->second;
11485           std::map<int, int>::iterator itdom = domvol.begin();
11486           int dom1 = itdom->first;
11487           int vtkVolId = itdom->second;
11488           itdom++;
11489           int dom2 = itdom->first;
11490           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11491                                                              nodeQuadDomains);
11492           stringstream grpname;
11493           grpname << "j_";
11494           if (dom1 < dom2)
11495             grpname << dom1 << "_" << dom2;
11496           else
11497             grpname << dom2 << "_" << dom1;
11498           string namegrp = grpname.str();
11499           if (!mapOfJunctionGroups.count(namegrp))
11500             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11501           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11502           if (sgrp)
11503             sgrp->Add(vol->GetID());
11504           if (vol->GetType() == SMDSAbs_Volume)
11505             joints3DGrp->Add(vol->GetID());
11506           else if (vol->GetType() == SMDSAbs_Face)
11507             joints2DGrp->Add(vol->GetID());
11508         }
11509     }
11510
11511   // --- create volumes on multiple domain intersection if requested
11512   //     iterate on mutipleNodesToFace
11513   //     iterate on edgesMultiDomains
11514
11515   if (createJointElems)
11516     {
11517       // --- iterate on mutipleNodesToFace
11518
11519       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11520       for (; itn != mutipleNodesToFace.end(); ++itn)
11521         {
11522           int node = itn->first;
11523           vector<int> orderDom = itn->second;
11524           vector<vtkIdType> orderedNodes;
11525           for (int idom = 0; idom <orderDom.size(); idom++)
11526             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11527             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11528
11529             stringstream grpname;
11530             grpname << "m2j_";
11531             grpname << 0 << "_" << 0;
11532             int idg;
11533             string namegrp = grpname.str();
11534             if (!mapOfJunctionGroups.count(namegrp))
11535               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11536             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11537             if (sgrp)
11538               sgrp->Add(face->GetID());
11539         }
11540
11541       // --- iterate on edgesMultiDomains
11542
11543       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11544       for (; ite != edgesMultiDomains.end(); ++ite)
11545         {
11546           vector<int> nodes = ite->first;
11547           vector<int> orderDom = ite->second;
11548           vector<vtkIdType> orderedNodes;
11549           if (nodes.size() == 2)
11550             {
11551               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11552               for (int ino=0; ino < nodes.size(); ino++)
11553                 if (orderDom.size() == 3)
11554                   for (int idom = 0; idom <orderDom.size(); idom++)
11555                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11556                 else
11557                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11558                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11559               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11560
11561               int idg;
11562               string namegrp = "jointsMultiples";
11563               if (!mapOfJunctionGroups.count(namegrp))
11564                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11565               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11566               if (sgrp)
11567                 sgrp->Add(vol->GetID());
11568             }
11569           else
11570             {
11571               INFOS("Quadratic multiple joints not implemented");
11572               // TODO quadratic nodes
11573             }
11574         }
11575     }
11576
11577   // --- list the explicit faces and edges of the mesh that need to be modified,
11578   //     i.e. faces and edges built with one or more duplicated nodes.
11579   //     associate these faces or edges to their corresponding domain.
11580   //     only the first domain found is kept when a face or edge is shared
11581
11582   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11583   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11584   faceOrEdgeDom.clear();
11585   feDom.clear();
11586
11587   for (int idomain = 0; idomain < theElems.size(); idomain++)
11588     {
11589       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11590       for (; itnod != nodeDomains.end(); ++itnod)
11591         {
11592           int oldId = itnod->first;
11593           //MESSAGE("     node " << oldId);
11594           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11595           for (int i = 0; i < l.ncells; i++)
11596             {
11597               int vtkId = l.cells[i];
11598               int vtkType = grid->GetCellType(vtkId);
11599               int downId = grid->CellIdToDownId(vtkId);
11600               if (downId < 0)
11601                 continue; // new cells: not to be modified
11602               DownIdType aCell(downId, vtkType);
11603               int volParents[1000];
11604               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11605               for (int j = 0; j < nbvol; j++)
11606                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11607                   if (!feDom.count(vtkId))
11608                     {
11609                       feDom[vtkId] = idomain;
11610                       faceOrEdgeDom[aCell] = emptyMap;
11611                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11612                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11613                       //        << " type " << vtkType << " downId " << downId);
11614                     }
11615             }
11616         }
11617     }
11618
11619   // --- iterate on shared faces (volumes to modify, face to extrude)
11620   //     get node id's of the face
11621   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11622
11623   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11624   for (int m=0; m<3; m++)
11625     {
11626       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11627       itface = (*amap).begin();
11628       for (; itface != (*amap).end(); ++itface)
11629         {
11630           DownIdType face = itface->first;
11631           std::set<int> oldNodes;
11632           std::set<int>::iterator itn;
11633           oldNodes.clear();
11634           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11635           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11636           std::map<int, int> localClonedNodeIds;
11637
11638           std::map<int, int> domvol = itface->second;
11639           std::map<int, int>::iterator itdom = domvol.begin();
11640           for (; itdom != domvol.end(); ++itdom)
11641             {
11642               int idom = itdom->first;
11643               int vtkVolId = itdom->second;
11644               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11645               localClonedNodeIds.clear();
11646               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11647                 {
11648                   int oldId = *itn;
11649                   if (nodeDomains[oldId].count(idom))
11650                     {
11651                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11652                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11653                     }
11654                 }
11655               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11656             }
11657         }
11658     }
11659
11660   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11661   grid->BuildLinks();
11662
11663   CHRONOSTOP(50);
11664   counters::stats();
11665   return true;
11666 }
11667
11668 /*!
11669  * \brief Double nodes on some external faces and create flat elements.
11670  * Flat elements are mainly used by some types of mechanic calculations.
11671  *
11672  * Each group of the list must be constituted of faces.
11673  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11674  * @param theElems - list of groups of faces, where a group of faces is a set of
11675  * SMDS_MeshElements sorted by Id.
11676  * @return TRUE if operation has been completed successfully, FALSE otherwise
11677  */
11678 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11679 {
11680   MESSAGE("-------------------------------------------------");
11681   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11682   MESSAGE("-------------------------------------------------");
11683
11684   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11685
11686   // --- For each group of faces
11687   //     duplicate the nodes, create a flat element based on the face
11688   //     replace the nodes of the faces by their clones
11689
11690   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11691   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11692   clonedNodes.clear();
11693   intermediateNodes.clear();
11694   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11695   mapOfJunctionGroups.clear();
11696
11697   for (int idom = 0; idom < theElems.size(); idom++)
11698     {
11699       const TIDSortedElemSet& domain = theElems[idom];
11700       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11701       for (; elemItr != domain.end(); ++elemItr)
11702         {
11703           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11704           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11705           if (!aFace)
11706             continue;
11707           // MESSAGE("aFace=" << aFace->GetID());
11708           bool isQuad = aFace->IsQuadratic();
11709           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11710
11711           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11712
11713           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11714           while (nodeIt->more())
11715             {
11716               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11717               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11718               if (isMedium)
11719                 ln2.push_back(node);
11720               else
11721                 ln0.push_back(node);
11722
11723               const SMDS_MeshNode* clone = 0;
11724               if (!clonedNodes.count(node))
11725                 {
11726                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11727                   clonedNodes[node] = clone;
11728                 }
11729               else
11730                 clone = clonedNodes[node];
11731
11732               if (isMedium)
11733                 ln3.push_back(clone);
11734               else
11735                 ln1.push_back(clone);
11736
11737               const SMDS_MeshNode* inter = 0;
11738               if (isQuad && (!isMedium))
11739                 {
11740                   if (!intermediateNodes.count(node))
11741                     {
11742                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11743                       intermediateNodes[node] = inter;
11744                     }
11745                   else
11746                     inter = intermediateNodes[node];
11747                   ln4.push_back(inter);
11748                 }
11749             }
11750
11751           // --- extrude the face
11752
11753           vector<const SMDS_MeshNode*> ln;
11754           SMDS_MeshVolume* vol = 0;
11755           vtkIdType aType = aFace->GetVtkType();
11756           switch (aType)
11757           {
11758             case VTK_TRIANGLE:
11759               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11760               // MESSAGE("vol prism " << vol->GetID());
11761               ln.push_back(ln1[0]);
11762               ln.push_back(ln1[1]);
11763               ln.push_back(ln1[2]);
11764               break;
11765             case VTK_QUAD:
11766               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11767               // MESSAGE("vol hexa " << vol->GetID());
11768               ln.push_back(ln1[0]);
11769               ln.push_back(ln1[1]);
11770               ln.push_back(ln1[2]);
11771               ln.push_back(ln1[3]);
11772               break;
11773             case VTK_QUADRATIC_TRIANGLE:
11774               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11775                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11776               // MESSAGE("vol quad prism " << vol->GetID());
11777               ln.push_back(ln1[0]);
11778               ln.push_back(ln1[1]);
11779               ln.push_back(ln1[2]);
11780               ln.push_back(ln3[0]);
11781               ln.push_back(ln3[1]);
11782               ln.push_back(ln3[2]);
11783               break;
11784             case VTK_QUADRATIC_QUAD:
11785 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11786 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11787 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11788               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11789                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11790                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11791               // MESSAGE("vol quad hexa " << vol->GetID());
11792               ln.push_back(ln1[0]);
11793               ln.push_back(ln1[1]);
11794               ln.push_back(ln1[2]);
11795               ln.push_back(ln1[3]);
11796               ln.push_back(ln3[0]);
11797               ln.push_back(ln3[1]);
11798               ln.push_back(ln3[2]);
11799               ln.push_back(ln3[3]);
11800               break;
11801             case VTK_POLYGON:
11802               break;
11803             default:
11804               break;
11805           }
11806
11807           if (vol)
11808             {
11809               stringstream grpname;
11810               grpname << "jf_";
11811               grpname << idom;
11812               int idg;
11813               string namegrp = grpname.str();
11814               if (!mapOfJunctionGroups.count(namegrp))
11815                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11816               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11817               if (sgrp)
11818                 sgrp->Add(vol->GetID());
11819             }
11820
11821           // --- modify the face
11822
11823           aFace->ChangeNodes(&ln[0], ln.size());
11824         }
11825     }
11826   return true;
11827 }
11828
11829 /*!
11830  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11831  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11832  *  groups of faces to remove inside the object, (idem edges).
11833  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11834  */
11835 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11836                                       const TopoDS_Shape& theShape,
11837                                       SMESH_NodeSearcher* theNodeSearcher,
11838                                       const char* groupName,
11839                                       std::vector<double>&   nodesCoords,
11840                                       std::vector<std::vector<int> >& listOfListOfNodes)
11841 {
11842   MESSAGE("--------------------------------");
11843   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11844   MESSAGE("--------------------------------");
11845
11846   // --- zone of volumes to remove is given :
11847   //     1 either by a geom shape (one or more vertices) and a radius,
11848   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11849   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11850   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11851   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11852   //     defined by it's name.
11853
11854   SMESHDS_GroupBase* groupDS = 0;
11855   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11856   while ( groupIt->more() )
11857     {
11858       groupDS = 0;
11859       SMESH_Group * group = groupIt->next();
11860       if ( !group ) continue;
11861       groupDS = group->GetGroupDS();
11862       if ( !groupDS || groupDS->IsEmpty() ) continue;
11863       std::string grpName = group->GetName();
11864       //MESSAGE("grpName=" << grpName);
11865       if (grpName == groupName)
11866         break;
11867       else
11868         groupDS = 0;
11869     }
11870
11871   bool isNodeGroup = false;
11872   bool isNodeCoords = false;
11873   if (groupDS)
11874     {
11875       if (groupDS->GetType() != SMDSAbs_Node)
11876         return;
11877       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11878     }
11879
11880   if (nodesCoords.size() > 0)
11881     isNodeCoords = true; // a list o nodes given by their coordinates
11882   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11883
11884   // --- define groups to build
11885
11886   int idg; // --- group of SMDS volumes
11887   string grpvName = groupName;
11888   grpvName += "_vol";
11889   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11890   if (!grp)
11891     {
11892       MESSAGE("group not created " << grpvName);
11893       return;
11894     }
11895   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11896
11897   int idgs; // --- group of SMDS faces on the skin
11898   string grpsName = groupName;
11899   grpsName += "_skin";
11900   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11901   if (!grps)
11902     {
11903       MESSAGE("group not created " << grpsName);
11904       return;
11905     }
11906   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11907
11908   int idgi; // --- group of SMDS faces internal (several shapes)
11909   string grpiName = groupName;
11910   grpiName += "_internalFaces";
11911   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11912   if (!grpi)
11913     {
11914       MESSAGE("group not created " << grpiName);
11915       return;
11916     }
11917   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11918
11919   int idgei; // --- group of SMDS faces internal (several shapes)
11920   string grpeiName = groupName;
11921   grpeiName += "_internalEdges";
11922   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11923   if (!grpei)
11924     {
11925       MESSAGE("group not created " << grpeiName);
11926       return;
11927     }
11928   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11929
11930   // --- build downward connectivity
11931
11932   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11933   meshDS->BuildDownWardConnectivity(true);
11934   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11935
11936   // --- set of volumes detected inside
11937
11938   std::set<int> setOfInsideVol;
11939   std::set<int> setOfVolToCheck;
11940
11941   std::vector<gp_Pnt> gpnts;
11942   gpnts.clear();
11943
11944   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11945     {
11946       MESSAGE("group of nodes provided");
11947       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11948       while ( elemIt->more() )
11949         {
11950           const SMDS_MeshElement* elem = elemIt->next();
11951           if (!elem)
11952             continue;
11953           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11954           if (!node)
11955             continue;
11956           SMDS_MeshElement* vol = 0;
11957           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11958           while (volItr->more())
11959             {
11960               vol = (SMDS_MeshElement*)volItr->next();
11961               setOfInsideVol.insert(vol->getVtkId());
11962               sgrp->Add(vol->GetID());
11963             }
11964         }
11965     }
11966   else if (isNodeCoords)
11967     {
11968       MESSAGE("list of nodes coordinates provided");
11969       int i = 0;
11970       int k = 0;
11971       while (i < nodesCoords.size()-2)
11972         {
11973           double x = nodesCoords[i++];
11974           double y = nodesCoords[i++];
11975           double z = nodesCoords[i++];
11976           gp_Pnt p = gp_Pnt(x, y ,z);
11977           gpnts.push_back(p);
11978           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11979         }
11980     }
11981   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11982     {
11983       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11984       TopTools_IndexedMapOfShape vertexMap;
11985       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11986       gp_Pnt p = gp_Pnt(0,0,0);
11987       if (vertexMap.Extent() < 1)
11988         return;
11989
11990       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11991         {
11992           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11993           p = BRep_Tool::Pnt(vertex);
11994           gpnts.push_back(p);
11995           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11996         }
11997     }
11998
11999   if (gpnts.size() > 0)
12000     {
12001       int nodeId = 0;
12002       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12003       if (startNode)
12004         nodeId = startNode->GetID();
12005       MESSAGE("nodeId " << nodeId);
12006
12007       double radius2 = radius*radius;
12008       MESSAGE("radius2 " << radius2);
12009
12010       // --- volumes on start node
12011
12012       setOfVolToCheck.clear();
12013       SMDS_MeshElement* startVol = 0;
12014       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12015       while (volItr->more())
12016         {
12017           startVol = (SMDS_MeshElement*)volItr->next();
12018           setOfVolToCheck.insert(startVol->getVtkId());
12019         }
12020       if (setOfVolToCheck.empty())
12021         {
12022           MESSAGE("No volumes found");
12023           return;
12024         }
12025
12026       // --- starting with central volumes then their neighbors, check if they are inside
12027       //     or outside the domain, until no more new neighbor volume is inside.
12028       //     Fill the group of inside volumes
12029
12030       std::map<int, double> mapOfNodeDistance2;
12031       mapOfNodeDistance2.clear();
12032       std::set<int> setOfOutsideVol;
12033       while (!setOfVolToCheck.empty())
12034         {
12035           std::set<int>::iterator it = setOfVolToCheck.begin();
12036           int vtkId = *it;
12037           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12038           bool volInside = false;
12039           vtkIdType npts = 0;
12040           vtkIdType* pts = 0;
12041           grid->GetCellPoints(vtkId, npts, pts);
12042           for (int i=0; i<npts; i++)
12043             {
12044               double distance2 = 0;
12045               if (mapOfNodeDistance2.count(pts[i]))
12046                 {
12047                   distance2 = mapOfNodeDistance2[pts[i]];
12048                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12049                 }
12050               else
12051                 {
12052                   double *coords = grid->GetPoint(pts[i]);
12053                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12054                   distance2 = 1.E40;
12055                   for (int j=0; j<gpnts.size(); j++)
12056                     {
12057                       double d2 = aPoint.SquareDistance(gpnts[j]);
12058                       if (d2 < distance2)
12059                         {
12060                           distance2 = d2;
12061                           if (distance2 < radius2)
12062                             break;
12063                         }
12064                     }
12065                   mapOfNodeDistance2[pts[i]] = distance2;
12066                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12067                 }
12068               if (distance2 < radius2)
12069                 {
12070                   volInside = true; // one or more nodes inside the domain
12071                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12072                   break;
12073                 }
12074             }
12075           if (volInside)
12076             {
12077               setOfInsideVol.insert(vtkId);
12078               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12079               int neighborsVtkIds[NBMAXNEIGHBORS];
12080               int downIds[NBMAXNEIGHBORS];
12081               unsigned char downTypes[NBMAXNEIGHBORS];
12082               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12083               for (int n = 0; n < nbNeighbors; n++)
12084                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12085                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12086             }
12087           else
12088             {
12089               setOfOutsideVol.insert(vtkId);
12090               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12091             }
12092           setOfVolToCheck.erase(vtkId);
12093         }
12094     }
12095
12096   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12097   //     If yes, add the volume to the inside set
12098
12099   bool addedInside = true;
12100   std::set<int> setOfVolToReCheck;
12101   while (addedInside)
12102     {
12103       MESSAGE(" --------------------------- re check");
12104       addedInside = false;
12105       std::set<int>::iterator itv = setOfInsideVol.begin();
12106       for (; itv != setOfInsideVol.end(); ++itv)
12107         {
12108           int vtkId = *itv;
12109           int neighborsVtkIds[NBMAXNEIGHBORS];
12110           int downIds[NBMAXNEIGHBORS];
12111           unsigned char downTypes[NBMAXNEIGHBORS];
12112           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12113           for (int n = 0; n < nbNeighbors; n++)
12114             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12115               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12116         }
12117       setOfVolToCheck = setOfVolToReCheck;
12118       setOfVolToReCheck.clear();
12119       while  (!setOfVolToCheck.empty())
12120         {
12121           std::set<int>::iterator it = setOfVolToCheck.begin();
12122           int vtkId = *it;
12123           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12124             {
12125               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12126               int countInside = 0;
12127               int neighborsVtkIds[NBMAXNEIGHBORS];
12128               int downIds[NBMAXNEIGHBORS];
12129               unsigned char downTypes[NBMAXNEIGHBORS];
12130               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12131               for (int n = 0; n < nbNeighbors; n++)
12132                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12133                   countInside++;
12134               MESSAGE("countInside " << countInside);
12135               if (countInside > 1)
12136                 {
12137                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12138                   setOfInsideVol.insert(vtkId);
12139                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12140                   addedInside = true;
12141                 }
12142               else
12143                 setOfVolToReCheck.insert(vtkId);
12144             }
12145           setOfVolToCheck.erase(vtkId);
12146         }
12147     }
12148
12149   // --- map of Downward faces at the boundary, inside the global volume
12150   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12151   //     fill group of SMDS faces inside the volume (when several volume shapes)
12152   //     fill group of SMDS faces on the skin of the global volume (if skin)
12153
12154   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12155   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12156   std::set<int>::iterator it = setOfInsideVol.begin();
12157   for (; it != setOfInsideVol.end(); ++it)
12158     {
12159       int vtkId = *it;
12160       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12161       int neighborsVtkIds[NBMAXNEIGHBORS];
12162       int downIds[NBMAXNEIGHBORS];
12163       unsigned char downTypes[NBMAXNEIGHBORS];
12164       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12165       for (int n = 0; n < nbNeighbors; n++)
12166         {
12167           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12168           if (neighborDim == 3)
12169             {
12170               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12171                 {
12172                   DownIdType face(downIds[n], downTypes[n]);
12173                   boundaryFaces[face] = vtkId;
12174                 }
12175               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12176               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12177               if (vtkFaceId >= 0)
12178                 {
12179                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12180                   // find also the smds edges on this face
12181                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12182                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12183                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12184                   for (int i = 0; i < nbEdges; i++)
12185                     {
12186                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12187                       if (vtkEdgeId >= 0)
12188                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12189                     }
12190                 }
12191             }
12192           else if (neighborDim == 2) // skin of the volume
12193             {
12194               DownIdType face(downIds[n], downTypes[n]);
12195               skinFaces[face] = vtkId;
12196               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12197               if (vtkFaceId >= 0)
12198                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12199             }
12200         }
12201     }
12202
12203   // --- identify the edges constituting the wire of each subshape on the skin
12204   //     define polylines with the nodes of edges, equivalent to wires
12205   //     project polylines on subshapes, and partition, to get geom faces
12206
12207   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12208   std::set<int> emptySet;
12209   emptySet.clear();
12210   std::set<int> shapeIds;
12211
12212   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12213   while (itelem->more())
12214     {
12215       const SMDS_MeshElement *elem = itelem->next();
12216       int shapeId = elem->getshapeId();
12217       int vtkId = elem->getVtkId();
12218       if (!shapeIdToVtkIdSet.count(shapeId))
12219         {
12220           shapeIdToVtkIdSet[shapeId] = emptySet;
12221           shapeIds.insert(shapeId);
12222         }
12223       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12224     }
12225
12226   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12227   std::set<DownIdType, DownIdCompare> emptyEdges;
12228   emptyEdges.clear();
12229
12230   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12231   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12232     {
12233       int shapeId = itShape->first;
12234       MESSAGE(" --- Shape ID --- "<< shapeId);
12235       shapeIdToEdges[shapeId] = emptyEdges;
12236
12237       std::vector<int> nodesEdges;
12238
12239       std::set<int>::iterator its = itShape->second.begin();
12240       for (; its != itShape->second.end(); ++its)
12241         {
12242           int vtkId = *its;
12243           MESSAGE("     " << vtkId);
12244           int neighborsVtkIds[NBMAXNEIGHBORS];
12245           int downIds[NBMAXNEIGHBORS];
12246           unsigned char downTypes[NBMAXNEIGHBORS];
12247           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12248           for (int n = 0; n < nbNeighbors; n++)
12249             {
12250               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12251                 continue;
12252               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12253               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12254               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12255                 {
12256                   DownIdType edge(downIds[n], downTypes[n]);
12257                   if (!shapeIdToEdges[shapeId].count(edge))
12258                     {
12259                       shapeIdToEdges[shapeId].insert(edge);
12260                       int vtkNodeId[3];
12261                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12262                       nodesEdges.push_back(vtkNodeId[0]);
12263                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12264                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12265                     }
12266                 }
12267             }
12268         }
12269
12270       std::list<int> order;
12271       order.clear();
12272       if (nodesEdges.size() > 0)
12273         {
12274           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12275           nodesEdges[0] = -1;
12276           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12277           nodesEdges[1] = -1; // do not reuse this edge
12278           bool found = true;
12279           while (found)
12280             {
12281               int nodeTofind = order.back(); // try first to push back
12282               int i = 0;
12283               for (i = 0; i<nodesEdges.size(); i++)
12284                 if (nodesEdges[i] == nodeTofind)
12285                   break;
12286               if (i == nodesEdges.size())
12287                 found = false; // no follower found on back
12288               else
12289                 {
12290                   if (i%2) // odd ==> use the previous one
12291                     if (nodesEdges[i-1] < 0)
12292                       found = false;
12293                     else
12294                       {
12295                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12296                         nodesEdges[i-1] = -1;
12297                       }
12298                   else // even ==> use the next one
12299                     if (nodesEdges[i+1] < 0)
12300                       found = false;
12301                     else
12302                       {
12303                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12304                         nodesEdges[i+1] = -1;
12305                       }
12306                 }
12307               if (found)
12308                 continue;
12309               // try to push front
12310               found = true;
12311               nodeTofind = order.front(); // try to push front
12312               for (i = 0; i<nodesEdges.size(); i++)
12313                 if (nodesEdges[i] == nodeTofind)
12314                   break;
12315               if (i == nodesEdges.size())
12316                 {
12317                   found = false; // no predecessor found on front
12318                   continue;
12319                 }
12320               if (i%2) // odd ==> use the previous one
12321                 if (nodesEdges[i-1] < 0)
12322                   found = false;
12323                 else
12324                   {
12325                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12326                     nodesEdges[i-1] = -1;
12327                   }
12328               else // even ==> use the next one
12329                 if (nodesEdges[i+1] < 0)
12330                   found = false;
12331                 else
12332                   {
12333                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12334                     nodesEdges[i+1] = -1;
12335                   }
12336             }
12337         }
12338
12339
12340       std::vector<int> nodes;
12341       nodes.push_back(shapeId);
12342       std::list<int>::iterator itl = order.begin();
12343       for (; itl != order.end(); itl++)
12344         {
12345           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12346           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12347         }
12348       listOfListOfNodes.push_back(nodes);
12349     }
12350
12351   //     partition geom faces with blocFissure
12352   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12353   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12354
12355   return;
12356 }
12357
12358
12359 //================================================================================
12360 /*!
12361  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12362  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12363  * \return TRUE if operation has been completed successfully, FALSE otherwise
12364  */
12365 //================================================================================
12366
12367 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12368 {
12369   // iterates on volume elements and detect all free faces on them
12370   SMESHDS_Mesh* aMesh = GetMeshDS();
12371   if (!aMesh)
12372     return false;
12373   //bool res = false;
12374   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12375   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12376   while(vIt->more())
12377   {
12378     const SMDS_MeshVolume* volume = vIt->next();
12379     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12380     vTool.SetExternalNormal();
12381     //const bool isPoly = volume->IsPoly();
12382     const int iQuad = volume->IsQuadratic();
12383     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12384     {
12385       if (!vTool.IsFreeFace(iface))
12386         continue;
12387       nbFree++;
12388       vector<const SMDS_MeshNode *> nodes;
12389       int nbFaceNodes = vTool.NbFaceNodes(iface);
12390       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12391       int inode = 0;
12392       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12393         nodes.push_back(faceNodes[inode]);
12394       if (iQuad) { // add medium nodes
12395         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12396           nodes.push_back(faceNodes[inode]);
12397         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12398           nodes.push_back(faceNodes[8]);
12399       }
12400       // add new face based on volume nodes
12401       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12402         nbExisted++;
12403         continue; // face already exsist
12404       }
12405       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12406       nbCreated++;
12407     }
12408   }
12409   return ( nbFree==(nbExisted+nbCreated) );
12410 }
12411
12412 namespace
12413 {
12414   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12415   {
12416     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12417       return n;
12418     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12419   }
12420 }
12421 //================================================================================
12422 /*!
12423  * \brief Creates missing boundary elements
12424  *  \param elements - elements whose boundary is to be checked
12425  *  \param dimension - defines type of boundary elements to create
12426  *  \param group - a group to store created boundary elements in
12427  *  \param targetMesh - a mesh to store created boundary elements in
12428  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12429  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12430  *                                boundary elements will be copied into the targetMesh
12431  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12432  *                                boundary elements will be added into the new group
12433  *  \param aroundElements - if true, elements will be created on boundary of given
12434  *                          elements else, on boundary of the whole mesh.
12435  * \return nb of added boundary elements
12436  */
12437 //================================================================================
12438
12439 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12440                                        Bnd_Dimension           dimension,
12441                                        SMESH_Group*            group/*=0*/,
12442                                        SMESH_Mesh*             targetMesh/*=0*/,
12443                                        bool                    toCopyElements/*=false*/,
12444                                        bool                    toCopyExistingBoundary/*=false*/,
12445                                        bool                    toAddExistingBondary/*= false*/,
12446                                        bool                    aroundElements/*= false*/)
12447 {
12448   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12449   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12450   // hope that all elements are of the same type, do not check them all
12451   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12452     throw SALOME_Exception(LOCALIZED("wrong element type"));
12453
12454   if ( !targetMesh )
12455     toCopyElements = toCopyExistingBoundary = false;
12456
12457   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12458   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12459   int nbAddedBnd = 0;
12460
12461   // editor adding present bnd elements and optionally holding elements to add to the group
12462   SMESH_MeshEditor* presentEditor;
12463   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12464   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12465
12466   SMESH_MesherHelper helper( *myMesh );
12467   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12468   SMDS_VolumeTool vTool;
12469   TIDSortedElemSet avoidSet;
12470   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12471   int inode;
12472
12473   typedef vector<const SMDS_MeshNode*> TConnectivity;
12474
12475   SMDS_ElemIteratorPtr eIt;
12476   if (elements.empty())
12477     eIt = aMesh->elementsIterator(elemType);
12478   else
12479     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12480
12481   while (eIt->more())
12482   {
12483     const SMDS_MeshElement* elem = eIt->next();
12484     const int iQuad = elem->IsQuadratic();
12485
12486     // ------------------------------------------------------------------------------------
12487     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12488     // ------------------------------------------------------------------------------------
12489     vector<const SMDS_MeshElement*> presentBndElems;
12490     vector<TConnectivity>           missingBndElems;
12491     TConnectivity nodes;
12492     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12493     {
12494       vTool.SetExternalNormal();
12495       const SMDS_MeshElement* otherVol = 0;
12496       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12497       {
12498         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12499              ( !aroundElements || elements.count( otherVol )))
12500           continue;
12501         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12502         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12503         if ( missType == SMDSAbs_Edge ) // boundary edges
12504         {
12505           nodes.resize( 2+iQuad );
12506           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12507           {
12508             for ( int j = 0; j < nodes.size(); ++j )
12509               nodes[j] =nn[i+j];
12510             if ( const SMDS_MeshElement* edge =
12511                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12512               presentBndElems.push_back( edge );
12513             else
12514               missingBndElems.push_back( nodes );
12515           }
12516         }
12517         else // boundary face
12518         {
12519           nodes.clear();
12520           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12521             nodes.push_back( nn[inode] );
12522           if (iQuad) // add medium nodes
12523             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12524               nodes.push_back( nn[inode] );
12525           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12526           if ( iCenter > 0 )
12527             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12528
12529           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12530                                                                SMDSAbs_Face, /*noMedium=*/false ))
12531             presentBndElems.push_back( f );
12532           else
12533             missingBndElems.push_back( nodes );
12534
12535           if ( targetMesh != myMesh )
12536           {
12537             // add 1D elements on face boundary to be added to a new mesh
12538             const SMDS_MeshElement* edge;
12539             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12540             {
12541               if ( iQuad )
12542                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12543               else
12544                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12545               if ( edge && avoidSet.insert( edge ).second )
12546                 presentBndElems.push_back( edge );
12547             }
12548           }
12549         }
12550       }
12551     }
12552     else                     // elem is a face ------------------------------------------
12553     {
12554       avoidSet.clear(), avoidSet.insert( elem );
12555       int nbNodes = elem->NbCornerNodes();
12556       nodes.resize( 2 /*+ iQuad*/);
12557       for ( int i = 0; i < nbNodes; i++ )
12558       {
12559         nodes[0] = elem->GetNode(i);
12560         nodes[1] = elem->GetNode((i+1)%nbNodes);
12561         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12562           continue; // not free link
12563
12564         //if ( iQuad )
12565         //nodes[2] = elem->GetNode( i + nbNodes );
12566         if ( const SMDS_MeshElement* edge =
12567              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12568           presentBndElems.push_back( edge );
12569         else
12570           missingBndElems.push_back( nodes );
12571       }
12572     }
12573
12574     // ---------------------------------
12575     // 2. Add missing boundary elements
12576     // ---------------------------------
12577     if ( targetMesh != myMesh )
12578       // instead of making a map of nodes in this mesh and targetMesh,
12579       // we create nodes with same IDs.
12580       for ( int i = 0; i < missingBndElems.size(); ++i )
12581       {
12582         TConnectivity& srcNodes = missingBndElems[i];
12583         TConnectivity  nodes( srcNodes.size() );
12584         for ( inode = 0; inode < nodes.size(); ++inode )
12585           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12586         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12587                                                                    missType,
12588                                                                    /*noMedium=*/false))
12589           continue;
12590         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12591         ++nbAddedBnd;
12592       }
12593     else
12594       for ( int i = 0; i < missingBndElems.size(); ++i )
12595       {
12596         TConnectivity& nodes = missingBndElems[i];
12597         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12598                                                                    missType,
12599                                                                    /*noMedium=*/false))
12600           continue;
12601         SMDS_MeshElement* elem =
12602           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12603         ++nbAddedBnd;
12604
12605         // try to set a new element to a shape
12606         if ( myMesh->HasShapeToMesh() )
12607         {
12608           bool ok = true;
12609           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12610           const int nbN = nodes.size() / (iQuad+1 );
12611           for ( inode = 0; inode < nbN && ok; ++inode )
12612           {
12613             pair<int, TopAbs_ShapeEnum> i_stype =
12614               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12615             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12616               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12617           }
12618           if ( ok && mediumShapes.size() > 1 )
12619           {
12620             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12621             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12622             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12623             {
12624               if (( ok = ( stype_i->first != stype_i_0.first )))
12625                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12626                                         aMesh->IndexToShape( stype_i_0.second ));
12627             }
12628           }
12629           if ( ok && mediumShapes.begin()->first == missShapeType )
12630             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12631         }
12632       }
12633
12634     // ----------------------------------
12635     // 3. Copy present boundary elements
12636     // ----------------------------------
12637     if ( toCopyExistingBoundary )
12638       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12639       {
12640         const SMDS_MeshElement* e = presentBndElems[i];
12641         TConnectivity nodes( e->NbNodes() );
12642         for ( inode = 0; inode < nodes.size(); ++inode )
12643           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12644         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12645       }
12646     else // store present elements to add them to a group
12647       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12648       {
12649         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12650       }
12651
12652   } // loop on given elements
12653
12654   // ---------------------------------------------
12655   // 4. Fill group with boundary elements
12656   // ---------------------------------------------
12657   if ( group )
12658   {
12659     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12660       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12661         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12662   }
12663   tgtEditor.myLastCreatedElems.Clear();
12664   tgtEditor2.myLastCreatedElems.Clear();
12665
12666   // -----------------------
12667   // 5. Copy given elements
12668   // -----------------------
12669   if ( toCopyElements && targetMesh != myMesh )
12670   {
12671     if (elements.empty())
12672       eIt = aMesh->elementsIterator(elemType);
12673     else
12674       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12675     while (eIt->more())
12676     {
12677       const SMDS_MeshElement* elem = eIt->next();
12678       TConnectivity nodes( elem->NbNodes() );
12679       for ( inode = 0; inode < nodes.size(); ++inode )
12680         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12681       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12682
12683       tgtEditor.myLastCreatedElems.Clear();
12684     }
12685   }
12686   return nbAddedBnd;
12687 }