Salome HOME
- theElems.insert( face );
[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 orientated 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   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1157   while ( startFace != startFaces.end() )
1158   {
1159     theFace = *startFace;
1160     const int nbNodes = theFace->NbCornerNodes();
1161
1162     avoidSet.clear();
1163     avoidSet.insert(theFace);
1164
1165     NLink link( theFace->GetNode( 0 ), 0 );
1166     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1167     {
1168       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1169       linkIt_isNew = checkedLinks.insert( link );
1170       if ( !linkIt_isNew.second )
1171       {
1172         // link has already been checked and won't be encountered more
1173         // if the group (theFaces) is manifold
1174         checkedLinks.erase( linkIt_isNew.first );
1175       }
1176       else
1177       {
1178         int nodeInd1, nodeInd2;
1179         const SMDS_MeshElement* otherFace = FindFaceInSet( link.first, link.second,
1180                                                            theFaces, avoidSet,
1181                                                            & nodeInd1, & nodeInd2);
1182         if ( otherFace && otherFace != theFace)
1183         {
1184           // link must be reversed in otherFace if orientation ot otherFace
1185           // is same as that of theFace
1186           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1187           {
1188             // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1189             //      << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1190             nbReori += Reorient( otherFace );
1191           }
1192           startFaces.insert( otherFace );
1193           if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1194             theFaces.erase( otherFace );
1195         }
1196       }
1197       std::swap( link.first, link.second );
1198     }
1199     startFaces.erase( startFace );
1200     startFace = startFaces.begin();
1201   }
1202   return nbReori;
1203 }
1204
1205 //=======================================================================
1206 //function : getBadRate
1207 //purpose  :
1208 //=======================================================================
1209
1210 static double getBadRate (const SMDS_MeshElement*               theElem,
1211                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1212 {
1213   SMESH::Controls::TSequenceOfXYZ P;
1214   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1215     return 1e100;
1216   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1217   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1218 }
1219
1220 //=======================================================================
1221 //function : QuadToTri
1222 //purpose  : Cut quadrangles into triangles.
1223 //           theCrit is used to select a diagonal to cut
1224 //=======================================================================
1225
1226 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1227                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1228 {
1229   myLastCreatedElems.Clear();
1230   myLastCreatedNodes.Clear();
1231
1232   MESSAGE( "::QuadToTri()" );
1233
1234   if ( !theCrit.get() )
1235     return false;
1236
1237   SMESHDS_Mesh * aMesh = GetMeshDS();
1238
1239   Handle(Geom_Surface) surface;
1240   SMESH_MesherHelper   helper( *GetMesh() );
1241
1242   TIDSortedElemSet::iterator itElem;
1243   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1244     const SMDS_MeshElement* elem = *itElem;
1245     if ( !elem || elem->GetType() != SMDSAbs_Face )
1246       continue;
1247     if ( elem->NbCornerNodes() != 4 )
1248       continue;
1249
1250     // retrieve element nodes
1251     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1252
1253     // compare two sets of possible triangles
1254     double aBadRate1, aBadRate2; // to what extent a set is bad
1255     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1256     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1257     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1258
1259     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1260     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1261     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1262
1263     int aShapeId = FindShape( elem );
1264     const SMDS_MeshElement* newElem1 = 0;
1265     const SMDS_MeshElement* newElem2 = 0;
1266
1267     if( !elem->IsQuadratic() ) {
1268
1269       // split liner quadrangle
1270       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1271       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1272       if ( aBadRate1 <= aBadRate2 ) {
1273         // tr1 + tr2 is better
1274         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1275         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1276       }
1277       else {
1278         // tr3 + tr4 is better
1279         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1280         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1281       }
1282     }
1283     else {
1284
1285       // split quadratic quadrangle
1286
1287       // get surface elem is on
1288       if ( aShapeId != helper.GetSubShapeID() ) {
1289         surface.Nullify();
1290         TopoDS_Shape shape;
1291         if ( aShapeId > 0 )
1292           shape = aMesh->IndexToShape( aShapeId );
1293         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1294           TopoDS_Face face = TopoDS::Face( shape );
1295           surface = BRep_Tool::Surface( face );
1296           if ( !surface.IsNull() )
1297             helper.SetSubShape( shape );
1298         }
1299       }
1300       // find middle point for (0,1,2,3)
1301       // and create a node in this point;
1302       const SMDS_MeshNode* newN = 0;
1303       if ( aNodes.size() == 9 )
1304       {
1305         // SMDSEntity_BiQuad_Quadrangle
1306         newN = aNodes.back();
1307       }
1308       else
1309       {
1310         gp_XYZ p( 0,0,0 );
1311         if ( surface.IsNull() )
1312         {
1313           for ( int i = 0; i < 4; i++ )
1314             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1315           p /= 4;
1316         }
1317         else
1318         {
1319           const SMDS_MeshNode* inFaceNode = 0;
1320           if ( helper.GetNodeUVneedInFaceNode() )
1321             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1322               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1323                 inFaceNode = aNodes[ i ];
1324
1325           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1326           gp_XY uv( 0,0 );
1327           for ( int i = 0; i < 4; i++ )
1328             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1329           uv /= 4.;
1330           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1331         }
1332         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1333         myLastCreatedNodes.Append(newN);
1334       }
1335       // create a new element
1336       if ( aBadRate1 <= aBadRate2 ) {
1337         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1338                                   aNodes[6], aNodes[7], newN );
1339         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1340                                   newN,      aNodes[4], aNodes[5] );
1341       }
1342       else {
1343         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1344                                   aNodes[7], aNodes[4], newN );
1345         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1346                                   newN,      aNodes[5], aNodes[6] );
1347       }
1348     } // quadratic case
1349
1350     // care of a new element
1351
1352     myLastCreatedElems.Append(newElem1);
1353     myLastCreatedElems.Append(newElem2);
1354     AddToSameGroups( newElem1, elem, aMesh );
1355     AddToSameGroups( newElem2, elem, aMesh );
1356
1357     // put a new triangle on the same shape
1358     if ( aShapeId )
1359       {
1360         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1361         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1362       }
1363     aMesh->RemoveElement( elem );
1364   }
1365   return true;
1366 }
1367
1368 //=======================================================================
1369 //function : BestSplit
1370 //purpose  : Find better diagonal for cutting.
1371 //=======================================================================
1372
1373 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1374                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1375 {
1376   myLastCreatedElems.Clear();
1377   myLastCreatedNodes.Clear();
1378
1379   if (!theCrit.get())
1380     return -1;
1381
1382   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1383     return -1;
1384
1385   if( theQuad->NbNodes()==4 ||
1386       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1387
1388     // retrieve element nodes
1389     const SMDS_MeshNode* aNodes [4];
1390     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1391     int i = 0;
1392     //while (itN->more())
1393     while (i<4) {
1394       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1395     }
1396     // compare two sets of possible triangles
1397     double aBadRate1, aBadRate2; // to what extent a set is bad
1398     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1399     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1400     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1401
1402     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1403     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1404     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1405     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1406     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1407     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1408       return 1; // diagonal 1-3
1409
1410     return 2; // diagonal 2-4
1411   }
1412   return -1;
1413 }
1414
1415 namespace
1416 {
1417   // Methods of splitting volumes into tetra
1418
1419   const int theHexTo5_1[5*4+1] =
1420     {
1421       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1422     };
1423   const int theHexTo5_2[5*4+1] =
1424     {
1425       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1426     };
1427   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1428
1429   const int theHexTo6_1[6*4+1] =
1430     {
1431       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
1432     };
1433   const int theHexTo6_2[6*4+1] =
1434     {
1435       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
1436     };
1437   const int theHexTo6_3[6*4+1] =
1438     {
1439       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
1440     };
1441   const int theHexTo6_4[6*4+1] =
1442     {
1443       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
1444     };
1445   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1446
1447   const int thePyraTo2_1[2*4+1] =
1448     {
1449       0, 1, 2, 4,    0, 2, 3, 4,   -1
1450     };
1451   const int thePyraTo2_2[2*4+1] =
1452     {
1453       1, 2, 3, 4,    1, 3, 0, 4,   -1
1454     };
1455   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1456
1457   const int thePentaTo3_1[3*4+1] =
1458     {
1459       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1460     };
1461   const int thePentaTo3_2[3*4+1] =
1462     {
1463       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1464     };
1465   const int thePentaTo3_3[3*4+1] =
1466     {
1467       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1468     };
1469   const int thePentaTo3_4[3*4+1] =
1470     {
1471       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1472     };
1473   const int thePentaTo3_5[3*4+1] =
1474     {
1475       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1476     };
1477   const int thePentaTo3_6[3*4+1] =
1478     {
1479       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1480     };
1481   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1482                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1483
1484   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1485   {
1486     int _n1, _n2, _n3;
1487     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1488     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1489     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1490   };
1491   struct TSplitMethod
1492   {
1493     int        _nbTetra;
1494     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1495     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1496     bool       _ownConn;      //!< to delete _connectivity in destructor
1497     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1498
1499     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1500       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1501     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1502     bool hasFacet( const TTriangleFacet& facet ) const
1503     {
1504       const int* tetConn = _connectivity;
1505       for ( ; tetConn[0] >= 0; tetConn += 4 )
1506         if (( facet.contains( tetConn[0] ) +
1507               facet.contains( tetConn[1] ) +
1508               facet.contains( tetConn[2] ) +
1509               facet.contains( tetConn[3] )) == 3 )
1510           return true;
1511       return false;
1512     }
1513   };
1514
1515   //=======================================================================
1516   /*!
1517    * \brief return TSplitMethod for the given element
1518    */
1519   //=======================================================================
1520
1521   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1522   {
1523     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1524
1525     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1526     // an edge and a face barycenter; tertaherdons are based on triangles and
1527     // a volume barycenter
1528     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1529
1530     // Find out how adjacent volumes are split
1531
1532     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1533     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1534     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1535     {
1536       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1537       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1538       if ( nbNodes < 4 ) continue;
1539
1540       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1541       const int* nInd = vol.GetFaceNodesIndices( iF );
1542       if ( nbNodes == 4 )
1543       {
1544         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1545         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1546         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1547         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1548       }
1549       else
1550       {
1551         int iCom = 0; // common node of triangle faces to split into
1552         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1553         {
1554           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1555                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1556                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1557           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1558                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1559                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1560           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1561           {
1562             triaSplits.push_back( t012 );
1563             triaSplits.push_back( t023 );
1564             break;
1565           }
1566         }
1567       }
1568       if ( !triaSplits.empty() )
1569         hasAdjacentSplits = true;
1570     }
1571
1572     // Among variants of split method select one compliant with adjacent volumes
1573
1574     TSplitMethod method;
1575     if ( !vol.Element()->IsPoly() && !is24TetMode )
1576     {
1577       int nbVariants = 2, nbTet = 0;
1578       const int** connVariants = 0;
1579       switch ( vol.Element()->GetEntityType() )
1580       {
1581       case SMDSEntity_Hexa:
1582       case SMDSEntity_Quad_Hexa:
1583       case SMDSEntity_TriQuad_Hexa:
1584         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1585           connVariants = theHexTo5, nbTet = 5;
1586         else
1587           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1588         break;
1589       case SMDSEntity_Pyramid:
1590       case SMDSEntity_Quad_Pyramid:
1591         connVariants = thePyraTo2;  nbTet = 2;
1592         break;
1593       case SMDSEntity_Penta:
1594       case SMDSEntity_Quad_Penta:
1595         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1596         break;
1597       default:
1598         nbVariants = 0;
1599       }
1600       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1601       {
1602         // check method compliancy with adjacent tetras,
1603         // all found splits must be among facets of tetras described by this method
1604         method = TSplitMethod( nbTet, connVariants[variant] );
1605         if ( hasAdjacentSplits && method._nbTetra > 0 )
1606         {
1607           bool facetCreated = true;
1608           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1609           {
1610             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1611             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1612               facetCreated = method.hasFacet( *facet );
1613           }
1614           if ( !facetCreated )
1615             method = TSplitMethod(0); // incompatible method
1616         }
1617       }
1618     }
1619     if ( method._nbTetra < 1 )
1620     {
1621       // No standard method is applicable, use a generic solution:
1622       // each facet of a volume is split into triangles and
1623       // each of triangles and a volume barycenter form a tetrahedron.
1624
1625       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1626
1627       int* connectivity = new int[ maxTetConnSize + 1 ];
1628       method._connectivity = connectivity;
1629       method._ownConn = true;
1630       method._baryNode = !isHex27; // to create central node or not
1631
1632       int connSize = 0;
1633       int baryCenInd = vol.NbNodes() - int( isHex27 );
1634       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1635       {
1636         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1637         const int*   nInd = vol.GetFaceNodesIndices( iF );
1638         // find common node of triangle facets of tetra to create
1639         int iCommon = 0; // index in linear numeration
1640         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1641         if ( !triaSplits.empty() )
1642         {
1643           // by found facets
1644           const TTriangleFacet* facet = &triaSplits.front();
1645           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1646             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1647                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1648               break;
1649         }
1650         else if ( nbNodes > 3 && !is24TetMode )
1651         {
1652           // find the best method of splitting into triangles by aspect ratio
1653           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1654           map< double, int > badness2iCommon;
1655           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1656           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1657           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1658           {
1659             double badness = 0;
1660             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1661             {
1662               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1663                                       nodes[ iQ*((iLast-1)%nbNodes)],
1664                                       nodes[ iQ*((iLast  )%nbNodes)]);
1665               badness += getBadRate( &tria, aspectRatio );
1666             }
1667             badness2iCommon.insert( make_pair( badness, iCommon ));
1668           }
1669           // use iCommon with lowest badness
1670           iCommon = badness2iCommon.begin()->second;
1671         }
1672         if ( iCommon >= nbNodes )
1673           iCommon = 0; // something wrong
1674
1675         // fill connectivity of tetrahedra based on a current face
1676         int nbTet = nbNodes - 2;
1677         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1678         {
1679           int faceBaryCenInd;
1680           if ( isHex27 )
1681           {
1682             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1683             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1684           }
1685           else
1686           {
1687             method._faceBaryNode[ iF ] = 0;
1688             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1689           }
1690           nbTet = nbNodes;
1691           for ( int i = 0; i < nbTet; ++i )
1692           {
1693             int i1 = i, i2 = (i+1) % nbNodes;
1694             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1695             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1696             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1697             connectivity[ connSize++ ] = faceBaryCenInd;
1698             connectivity[ connSize++ ] = baryCenInd;
1699           }
1700         }
1701         else
1702         {
1703           for ( int i = 0; i < nbTet; ++i )
1704           {
1705             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1706             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1707             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1708             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1709             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1710             connectivity[ connSize++ ] = baryCenInd;
1711           }
1712         }
1713         method._nbTetra += nbTet;
1714
1715       } // loop on volume faces
1716
1717       connectivity[ connSize++ ] = -1;
1718
1719     } // end of generic solution
1720
1721     return method;
1722   }
1723   //================================================================================
1724   /*!
1725    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1726    */
1727   //================================================================================
1728
1729   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1730   {
1731     // find the tetrahedron including the three nodes of facet
1732     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1733     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1734     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1735     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1736     while ( volIt1->more() )
1737     {
1738       const SMDS_MeshElement* v = volIt1->next();
1739       SMDSAbs_EntityType type = v->GetEntityType();
1740       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1741         continue;
1742       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1743         continue; // medium node not allowed
1744       const int ind2 = v->GetNodeIndex( n2 );
1745       if ( ind2 < 0 || 3 < ind2 )
1746         continue;
1747       const int ind3 = v->GetNodeIndex( n3 );
1748       if ( ind3 < 0 || 3 < ind3 )
1749         continue;
1750       return true;
1751     }
1752     return false;
1753   }
1754
1755   //=======================================================================
1756   /*!
1757    * \brief A key of a face of volume
1758    */
1759   //=======================================================================
1760
1761   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1762   {
1763     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1764     {
1765       TIDSortedNodeSet sortedNodes;
1766       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1767       int nbNodes = vol.NbFaceNodes( iF );
1768       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1769       for ( int i = 0; i < nbNodes; i += iQ )
1770         sortedNodes.insert( fNodes[i] );
1771       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1772       first.first   = (*(n++))->GetID();
1773       first.second  = (*(n++))->GetID();
1774       second.first  = (*(n++))->GetID();
1775       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1776     }
1777   };
1778 } // namespace
1779
1780 //=======================================================================
1781 //function : SplitVolumesIntoTetra
1782 //purpose  : Split volume elements into tetrahedra.
1783 //=======================================================================
1784
1785 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1786                                               const int                theMethodFlags)
1787 {
1788   // std-like iterator on coordinates of nodes of mesh element
1789   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1790   NXyzIterator xyzEnd;
1791
1792   SMDS_VolumeTool    volTool;
1793   SMESH_MesherHelper helper( *GetMesh());
1794
1795   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1796   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1797
1798   SMESH_SequenceOfElemPtr newNodes, newElems;
1799
1800   // map face of volume to it's baricenrtic node
1801   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1802   double bc[3];
1803
1804   TIDSortedElemSet::const_iterator elem = theElems.begin();
1805   for ( ; elem != theElems.end(); ++elem )
1806   {
1807     if ( (*elem)->GetType() != SMDSAbs_Volume )
1808       continue;
1809     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1810     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1811       continue;
1812
1813     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1814
1815     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1816     if ( splitMethod._nbTetra < 1 ) continue;
1817
1818     // find submesh to add new tetras to
1819     if ( !subMesh || !subMesh->Contains( *elem ))
1820     {
1821       int shapeID = FindShape( *elem );
1822       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1823       subMesh = GetMeshDS()->MeshElements( shapeID );
1824     }
1825     int iQ;
1826     if ( (*elem)->IsQuadratic() )
1827     {
1828       iQ = 2;
1829       // add quadratic links to the helper
1830       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1831       {
1832         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1833         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1834         for ( int iN = 0; iN < nbN; iN += iQ )
1835           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1836       }
1837       helper.SetIsQuadratic( true );
1838     }
1839     else
1840     {
1841       iQ = 1;
1842       helper.SetIsQuadratic( false );
1843     }
1844     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1845     helper.SetElementsOnShape( true );
1846     if ( splitMethod._baryNode )
1847     {
1848       // make a node at barycenter
1849       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1850       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1851       nodes.push_back( gcNode );
1852       newNodes.Append( gcNode );
1853     }
1854     if ( !splitMethod._faceBaryNode.empty() )
1855     {
1856       // make or find baricentric nodes of faces
1857       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1858       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1859       {
1860         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1861           volFace2BaryNode.insert
1862           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1863         if ( !f_n->second )
1864         {
1865           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1866           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1867         }
1868         nodes.push_back( iF_n->second = f_n->second );
1869       }
1870     }
1871
1872     // make tetras
1873     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1874     const int* tetConn = splitMethod._connectivity;
1875     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1876       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1877                                                        nodes[ tetConn[1] ],
1878                                                        nodes[ tetConn[2] ],
1879                                                        nodes[ tetConn[3] ]));
1880
1881     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1882
1883     // Split faces on sides of the split volume
1884
1885     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1886     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1887     {
1888       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1889       if ( nbNodes < 4 ) continue;
1890
1891       // find an existing face
1892       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1893                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1894       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1895                                                                        /*noMedium=*/false))
1896       {
1897         // make triangles
1898         helper.SetElementsOnShape( false );
1899         vector< const SMDS_MeshElement* > triangles;
1900
1901         // find submesh to add new triangles in
1902         if ( !fSubMesh || !fSubMesh->Contains( face ))
1903         {
1904           int shapeID = FindShape( face );
1905           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1906         }
1907         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1908         if ( iF_n != splitMethod._faceBaryNode.end() )
1909         {
1910           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1911           {
1912             const SMDS_MeshNode* n1 = fNodes[iN];
1913             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1914             const SMDS_MeshNode *n3 = iF_n->second;
1915             if ( !volTool.IsFaceExternal( iF ))
1916               swap( n2, n3 );
1917             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1918
1919             if ( fSubMesh && n3->getshapeId() < 1 )
1920               fSubMesh->AddNode( n3 );
1921           }
1922         }
1923         else
1924         {
1925           // among possible triangles create ones discribed by split method
1926           const int* nInd = volTool.GetFaceNodesIndices( iF );
1927           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1928           int iCom = 0; // common node of triangle faces to split into
1929           list< TTriangleFacet > facets;
1930           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1931           {
1932             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1933                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1934                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1935             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1936                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1937                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1938             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1939             {
1940               facets.push_back( t012 );
1941               facets.push_back( t023 );
1942               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1943                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1944                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1945                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1946               break;
1947             }
1948           }
1949           list< TTriangleFacet >::iterator facet = facets.begin();
1950           for ( ; facet != facets.end(); ++facet )
1951           {
1952             if ( !volTool.IsFaceExternal( iF ))
1953               swap( facet->_n2, facet->_n3 );
1954             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1955                                                  volNodes[ facet->_n2 ],
1956                                                  volNodes[ facet->_n3 ]));
1957           }
1958         }
1959         for ( int i = 0; i < triangles.size(); ++i )
1960         {
1961           if ( !triangles[i] ) continue;
1962           if ( fSubMesh )
1963             fSubMesh->AddElement( triangles[i]);
1964           newElems.Append( triangles[i] );
1965         }
1966         ReplaceElemInGroups( face, triangles, GetMeshDS() );
1967         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1968       }
1969
1970     } // loop on volume faces to split them into triangles
1971
1972     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1973
1974     if ( geomType == SMDSEntity_TriQuad_Hexa )
1975     {
1976       // remove medium nodes that could become free
1977       for ( int i = 20; i < volTool.NbNodes(); ++i )
1978         if ( volNodes[i]->NbInverseElements() == 0 )
1979           GetMeshDS()->RemoveNode( volNodes[i] );
1980     }
1981   } // loop on volumes to split
1982
1983   myLastCreatedNodes = newNodes;
1984   myLastCreatedElems = newElems;
1985 }
1986
1987 //=======================================================================
1988 //function : AddToSameGroups
1989 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
1990 //=======================================================================
1991
1992 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1993                                         const SMDS_MeshElement* elemInGroups,
1994                                         SMESHDS_Mesh *          aMesh)
1995 {
1996   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1997   if (!groups.empty()) {
1998     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1999     for ( ; grIt != groups.end(); grIt++ ) {
2000       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2001       if ( group && group->Contains( elemInGroups ))
2002         group->SMDSGroup().Add( elemToAdd );
2003     }
2004   }
2005 }
2006
2007
2008 //=======================================================================
2009 //function : RemoveElemFromGroups
2010 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2011 //=======================================================================
2012 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2013                                              SMESHDS_Mesh *          aMesh)
2014 {
2015   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2016   if (!groups.empty())
2017   {
2018     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2019     for (; GrIt != groups.end(); GrIt++)
2020     {
2021       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2022       if (!grp || grp->IsEmpty()) continue;
2023       grp->SMDSGroup().Remove(removeelem);
2024     }
2025   }
2026 }
2027
2028 //================================================================================
2029 /*!
2030  * \brief Replace elemToRm by elemToAdd in the all groups
2031  */
2032 //================================================================================
2033
2034 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2035                                             const SMDS_MeshElement* elemToAdd,
2036                                             SMESHDS_Mesh *          aMesh)
2037 {
2038   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2039   if (!groups.empty()) {
2040     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2041     for ( ; grIt != groups.end(); grIt++ ) {
2042       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2043       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2044         group->SMDSGroup().Add( elemToAdd );
2045     }
2046   }
2047 }
2048
2049 //================================================================================
2050 /*!
2051  * \brief Replace elemToRm by elemToAdd in the all groups
2052  */
2053 //================================================================================
2054
2055 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2056                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2057                                             SMESHDS_Mesh *                         aMesh)
2058 {
2059   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2060   if (!groups.empty())
2061   {
2062     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2063     for ( ; grIt != groups.end(); grIt++ ) {
2064       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2065       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2066         for ( int i = 0; i < elemToAdd.size(); ++i )
2067           group->SMDSGroup().Add( elemToAdd[ i ] );
2068     }
2069   }
2070 }
2071
2072 //=======================================================================
2073 //function : QuadToTri
2074 //purpose  : Cut quadrangles into triangles.
2075 //           theCrit is used to select a diagonal to cut
2076 //=======================================================================
2077
2078 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2079                                   const bool         the13Diag)
2080 {
2081   myLastCreatedElems.Clear();
2082   myLastCreatedNodes.Clear();
2083
2084   MESSAGE( "::QuadToTri()" );
2085
2086   SMESHDS_Mesh * aMesh = GetMeshDS();
2087
2088   Handle(Geom_Surface) surface;
2089   SMESH_MesherHelper   helper( *GetMesh() );
2090
2091   TIDSortedElemSet::iterator itElem;
2092   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2093     const SMDS_MeshElement* elem = *itElem;
2094     if ( !elem || elem->GetType() != SMDSAbs_Face )
2095       continue;
2096     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2097     if(!isquad) continue;
2098
2099     if(elem->NbNodes()==4) {
2100       // retrieve element nodes
2101       const SMDS_MeshNode* aNodes [4];
2102       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2103       int i = 0;
2104       while ( itN->more() )
2105         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2106
2107       int aShapeId = FindShape( elem );
2108       const SMDS_MeshElement* newElem1 = 0;
2109       const SMDS_MeshElement* newElem2 = 0;
2110       if ( the13Diag ) {
2111         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2112         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2113       }
2114       else {
2115         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2116         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2117       }
2118       myLastCreatedElems.Append(newElem1);
2119       myLastCreatedElems.Append(newElem2);
2120       // put a new triangle on the same shape and add to the same groups
2121       if ( aShapeId )
2122         {
2123           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2124           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2125         }
2126       AddToSameGroups( newElem1, elem, aMesh );
2127       AddToSameGroups( newElem2, elem, aMesh );
2128       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2129       aMesh->RemoveElement( elem );
2130     }
2131
2132     // Quadratic quadrangle
2133
2134     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2135
2136       // get surface elem is on
2137       int aShapeId = FindShape( elem );
2138       if ( aShapeId != helper.GetSubShapeID() ) {
2139         surface.Nullify();
2140         TopoDS_Shape shape;
2141         if ( aShapeId > 0 )
2142           shape = aMesh->IndexToShape( aShapeId );
2143         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2144           TopoDS_Face face = TopoDS::Face( shape );
2145           surface = BRep_Tool::Surface( face );
2146           if ( !surface.IsNull() )
2147             helper.SetSubShape( shape );
2148         }
2149       }
2150
2151       const SMDS_MeshNode* aNodes [8];
2152       const SMDS_MeshNode* inFaceNode = 0;
2153       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2154       int i = 0;
2155       while ( itN->more() ) {
2156         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2157         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2158              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2159         {
2160           inFaceNode = aNodes[ i-1 ];
2161         }
2162       }
2163
2164       // find middle point for (0,1,2,3)
2165       // and create a node in this point;
2166       gp_XYZ p( 0,0,0 );
2167       if ( surface.IsNull() ) {
2168         for(i=0; i<4; i++)
2169           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2170         p /= 4;
2171       }
2172       else {
2173         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2174         gp_XY uv( 0,0 );
2175         for(i=0; i<4; i++)
2176           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2177         uv /= 4.;
2178         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2179       }
2180       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2181       myLastCreatedNodes.Append(newN);
2182
2183       // create a new element
2184       const SMDS_MeshElement* newElem1 = 0;
2185       const SMDS_MeshElement* newElem2 = 0;
2186       if ( the13Diag ) {
2187         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2188                                   aNodes[6], aNodes[7], newN );
2189         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2190                                   newN,      aNodes[4], aNodes[5] );
2191       }
2192       else {
2193         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2194                                   aNodes[7], aNodes[4], newN );
2195         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2196                                   newN,      aNodes[5], aNodes[6] );
2197       }
2198       myLastCreatedElems.Append(newElem1);
2199       myLastCreatedElems.Append(newElem2);
2200       // put a new triangle on the same shape and add to the same groups
2201       if ( aShapeId )
2202         {
2203           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2204           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2205         }
2206       AddToSameGroups( newElem1, elem, aMesh );
2207       AddToSameGroups( newElem2, elem, aMesh );
2208       aMesh->RemoveElement( elem );
2209     }
2210   }
2211
2212   return true;
2213 }
2214
2215 //=======================================================================
2216 //function : getAngle
2217 //purpose  :
2218 //=======================================================================
2219
2220 double getAngle(const SMDS_MeshElement * tr1,
2221                 const SMDS_MeshElement * tr2,
2222                 const SMDS_MeshNode *    n1,
2223                 const SMDS_MeshNode *    n2)
2224 {
2225   double angle = 2. * M_PI; // bad angle
2226
2227   // get normals
2228   SMESH::Controls::TSequenceOfXYZ P1, P2;
2229   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2230        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2231     return angle;
2232   gp_Vec N1,N2;
2233   if(!tr1->IsQuadratic())
2234     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2235   else
2236     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2237   if ( N1.SquareMagnitude() <= gp::Resolution() )
2238     return angle;
2239   if(!tr2->IsQuadratic())
2240     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2241   else
2242     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2243   if ( N2.SquareMagnitude() <= gp::Resolution() )
2244     return angle;
2245
2246   // find the first diagonal node n1 in the triangles:
2247   // take in account a diagonal link orientation
2248   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2249   for ( int t = 0; t < 2; t++ ) {
2250     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2251     int i = 0, iDiag = -1;
2252     while ( it->more()) {
2253       const SMDS_MeshElement *n = it->next();
2254       if ( n == n1 || n == n2 ) {
2255         if ( iDiag < 0)
2256           iDiag = i;
2257         else {
2258           if ( i - iDiag == 1 )
2259             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2260           else
2261             nFirst[ t ] = n;
2262           break;
2263         }
2264       }
2265       i++;
2266     }
2267   }
2268   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2269     N2.Reverse();
2270
2271   angle = N1.Angle( N2 );
2272   //SCRUTE( angle );
2273   return angle;
2274 }
2275
2276 // =================================================
2277 // class generating a unique ID for a pair of nodes
2278 // and able to return nodes by that ID
2279 // =================================================
2280 class LinkID_Gen {
2281 public:
2282
2283   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2284     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2285   {}
2286
2287   long GetLinkID (const SMDS_MeshNode * n1,
2288                   const SMDS_MeshNode * n2) const
2289   {
2290     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2291   }
2292
2293   bool GetNodes (const long             theLinkID,
2294                  const SMDS_MeshNode* & theNode1,
2295                  const SMDS_MeshNode* & theNode2) const
2296   {
2297     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2298     if ( !theNode1 ) return false;
2299     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2300     if ( !theNode2 ) return false;
2301     return true;
2302   }
2303
2304 private:
2305   LinkID_Gen();
2306   const SMESHDS_Mesh* myMesh;
2307   long                myMaxID;
2308 };
2309
2310
2311 //=======================================================================
2312 //function : TriToQuad
2313 //purpose  : Fuse neighbour triangles into quadrangles.
2314 //           theCrit is used to select a neighbour to fuse with.
2315 //           theMaxAngle is a max angle between element normals at which
2316 //           fusion is still performed.
2317 //=======================================================================
2318
2319 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2320                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2321                                   const double                         theMaxAngle)
2322 {
2323   myLastCreatedElems.Clear();
2324   myLastCreatedNodes.Clear();
2325
2326   MESSAGE( "::TriToQuad()" );
2327
2328   if ( !theCrit.get() )
2329     return false;
2330
2331   SMESHDS_Mesh * aMesh = GetMeshDS();
2332
2333   // Prepare data for algo: build
2334   // 1. map of elements with their linkIDs
2335   // 2. map of linkIDs with their elements
2336
2337   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2338   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2339   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2340   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2341
2342   TIDSortedElemSet::iterator itElem;
2343   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2344     const SMDS_MeshElement* elem = *itElem;
2345     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2346     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2347     if(!IsTria) continue;
2348
2349     // retrieve element nodes
2350     const SMDS_MeshNode* aNodes [4];
2351     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2352     int i = 0;
2353     while ( i<3 )
2354       aNodes[ i++ ] = cast2Node( itN->next() );
2355     aNodes[ 3 ] = aNodes[ 0 ];
2356
2357     // fill maps
2358     for ( i = 0; i < 3; i++ ) {
2359       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2360       // check if elements sharing a link can be fused
2361       itLE = mapLi_listEl.find( link );
2362       if ( itLE != mapLi_listEl.end() ) {
2363         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2364           continue;
2365         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2366         //if ( FindShape( elem ) != FindShape( elem2 ))
2367         //  continue; // do not fuse triangles laying on different shapes
2368         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2369           continue; // avoid making badly shaped quads
2370         (*itLE).second.push_back( elem );
2371       }
2372       else {
2373         mapLi_listEl[ link ].push_back( elem );
2374       }
2375       mapEl_setLi [ elem ].insert( link );
2376     }
2377   }
2378   // Clean the maps from the links shared by a sole element, ie
2379   // links to which only one element is bound in mapLi_listEl
2380
2381   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2382     int nbElems = (*itLE).second.size();
2383     if ( nbElems < 2  ) {
2384       const SMDS_MeshElement* elem = (*itLE).second.front();
2385       SMESH_TLink link = (*itLE).first;
2386       mapEl_setLi[ elem ].erase( link );
2387       if ( mapEl_setLi[ elem ].empty() )
2388         mapEl_setLi.erase( elem );
2389     }
2390   }
2391
2392   // Algo: fuse triangles into quadrangles
2393
2394   while ( ! mapEl_setLi.empty() ) {
2395     // Look for the start element:
2396     // the element having the least nb of shared links
2397     const SMDS_MeshElement* startElem = 0;
2398     int minNbLinks = 4;
2399     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2400       int nbLinks = (*itEL).second.size();
2401       if ( nbLinks < minNbLinks ) {
2402         startElem = (*itEL).first;
2403         minNbLinks = nbLinks;
2404         if ( minNbLinks == 1 )
2405           break;
2406       }
2407     }
2408
2409     // search elements to fuse starting from startElem or links of elements
2410     // fused earlyer - startLinks
2411     list< SMESH_TLink > startLinks;
2412     while ( startElem || !startLinks.empty() ) {
2413       while ( !startElem && !startLinks.empty() ) {
2414         // Get an element to start, by a link
2415         SMESH_TLink linkId = startLinks.front();
2416         startLinks.pop_front();
2417         itLE = mapLi_listEl.find( linkId );
2418         if ( itLE != mapLi_listEl.end() ) {
2419           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2420           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2421           for ( ; itE != listElem.end() ; itE++ )
2422             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2423               startElem = (*itE);
2424           mapLi_listEl.erase( itLE );
2425         }
2426       }
2427
2428       if ( startElem ) {
2429         // Get candidates to be fused
2430         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2431         const SMESH_TLink *link12, *link13;
2432         startElem = 0;
2433         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2434         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2435         ASSERT( !setLi.empty() );
2436         set< SMESH_TLink >::iterator itLi;
2437         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2438         {
2439           const SMESH_TLink & link = (*itLi);
2440           itLE = mapLi_listEl.find( link );
2441           if ( itLE == mapLi_listEl.end() )
2442             continue;
2443
2444           const SMDS_MeshElement* elem = (*itLE).second.front();
2445           if ( elem == tr1 )
2446             elem = (*itLE).second.back();
2447           mapLi_listEl.erase( itLE );
2448           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2449             continue;
2450           if ( tr2 ) {
2451             tr3 = elem;
2452             link13 = &link;
2453           }
2454           else {
2455             tr2 = elem;
2456             link12 = &link;
2457           }
2458
2459           // add other links of elem to list of links to re-start from
2460           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2461           set< SMESH_TLink >::iterator it;
2462           for ( it = links.begin(); it != links.end(); it++ ) {
2463             const SMESH_TLink& link2 = (*it);
2464             if ( link2 != link )
2465               startLinks.push_back( link2 );
2466           }
2467         }
2468
2469         // Get nodes of possible quadrangles
2470         const SMDS_MeshNode *n12 [4], *n13 [4];
2471         bool Ok12 = false, Ok13 = false;
2472         const SMDS_MeshNode *linkNode1, *linkNode2;
2473         if(tr2) {
2474           linkNode1 = link12->first;
2475           linkNode2 = link12->second;
2476           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2477             Ok12 = true;
2478         }
2479         if(tr3) {
2480           linkNode1 = link13->first;
2481           linkNode2 = link13->second;
2482           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2483             Ok13 = true;
2484         }
2485
2486         // Choose a pair to fuse
2487         if ( Ok12 && Ok13 ) {
2488           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2489           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2490           double aBadRate12 = getBadRate( &quad12, theCrit );
2491           double aBadRate13 = getBadRate( &quad13, theCrit );
2492           if (  aBadRate13 < aBadRate12 )
2493             Ok12 = false;
2494           else
2495             Ok13 = false;
2496         }
2497
2498         // Make quadrangles
2499         // and remove fused elems and removed links from the maps
2500         mapEl_setLi.erase( tr1 );
2501         if ( Ok12 ) {
2502           mapEl_setLi.erase( tr2 );
2503           mapLi_listEl.erase( *link12 );
2504           if(tr1->NbNodes()==3) {
2505             const SMDS_MeshElement* newElem = 0;
2506             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2507             myLastCreatedElems.Append(newElem);
2508             AddToSameGroups( newElem, tr1, aMesh );
2509             int aShapeId = tr1->getshapeId();
2510             if ( aShapeId )
2511               {
2512                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2513               }
2514             aMesh->RemoveElement( tr1 );
2515             aMesh->RemoveElement( tr2 );
2516           }
2517           else {
2518             const SMDS_MeshNode* N1 [6];
2519             const SMDS_MeshNode* N2 [6];
2520             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2521             // now we receive following N1 and N2 (using numeration as above image)
2522             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2523             // i.e. first nodes from both arrays determ new diagonal
2524             const SMDS_MeshNode* aNodes[8];
2525             aNodes[0] = N1[0];
2526             aNodes[1] = N1[1];
2527             aNodes[2] = N2[0];
2528             aNodes[3] = N2[1];
2529             aNodes[4] = N1[3];
2530             aNodes[5] = N2[5];
2531             aNodes[6] = N2[3];
2532             aNodes[7] = N1[5];
2533             const SMDS_MeshElement* newElem = 0;
2534             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2535                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2536             myLastCreatedElems.Append(newElem);
2537             AddToSameGroups( newElem, tr1, aMesh );
2538             int aShapeId = tr1->getshapeId();
2539             if ( aShapeId )
2540               {
2541                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2542               }
2543             aMesh->RemoveElement( tr1 );
2544             aMesh->RemoveElement( tr2 );
2545             // remove middle node (9)
2546             GetMeshDS()->RemoveNode( N1[4] );
2547           }
2548         }
2549         else if ( Ok13 ) {
2550           mapEl_setLi.erase( tr3 );
2551           mapLi_listEl.erase( *link13 );
2552           if(tr1->NbNodes()==3) {
2553             const SMDS_MeshElement* newElem = 0;
2554             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2555             myLastCreatedElems.Append(newElem);
2556             AddToSameGroups( newElem, tr1, aMesh );
2557             int aShapeId = tr1->getshapeId();
2558             if ( aShapeId )
2559               {
2560                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2561               }
2562             aMesh->RemoveElement( tr1 );
2563             aMesh->RemoveElement( tr3 );
2564           }
2565           else {
2566             const SMDS_MeshNode* N1 [6];
2567             const SMDS_MeshNode* N2 [6];
2568             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2569             // now we receive following N1 and N2 (using numeration as above image)
2570             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2571             // i.e. first nodes from both arrays determ new diagonal
2572             const SMDS_MeshNode* aNodes[8];
2573             aNodes[0] = N1[0];
2574             aNodes[1] = N1[1];
2575             aNodes[2] = N2[0];
2576             aNodes[3] = N2[1];
2577             aNodes[4] = N1[3];
2578             aNodes[5] = N2[5];
2579             aNodes[6] = N2[3];
2580             aNodes[7] = N1[5];
2581             const SMDS_MeshElement* newElem = 0;
2582             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2583                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2584             myLastCreatedElems.Append(newElem);
2585             AddToSameGroups( newElem, tr1, aMesh );
2586             int aShapeId = tr1->getshapeId();
2587             if ( aShapeId )
2588               {
2589                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2590               }
2591             aMesh->RemoveElement( tr1 );
2592             aMesh->RemoveElement( tr3 );
2593             // remove middle node (9)
2594             GetMeshDS()->RemoveNode( N1[4] );
2595           }
2596         }
2597
2598         // Next element to fuse: the rejected one
2599         if ( tr3 )
2600           startElem = Ok12 ? tr3 : tr2;
2601
2602       } // if ( startElem )
2603     } // while ( startElem || !startLinks.empty() )
2604   } // while ( ! mapEl_setLi.empty() )
2605
2606   return true;
2607 }
2608
2609
2610 /*#define DUMPSO(txt) \
2611 //  cout << txt << endl;
2612 //=============================================================================
2613 //
2614 //
2615 //
2616 //=============================================================================
2617 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2618 {
2619 if ( i1 == i2 )
2620 return;
2621 int tmp = idNodes[ i1 ];
2622 idNodes[ i1 ] = idNodes[ i2 ];
2623 idNodes[ i2 ] = tmp;
2624 gp_Pnt Ptmp = P[ i1 ];
2625 P[ i1 ] = P[ i2 ];
2626 P[ i2 ] = Ptmp;
2627 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2628 }
2629
2630 //=======================================================================
2631 //function : SortQuadNodes
2632 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2633 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2634 //           1 or 2 else 0.
2635 //=======================================================================
2636
2637 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2638 int               idNodes[] )
2639 {
2640   gp_Pnt P[4];
2641   int i;
2642   for ( i = 0; i < 4; i++ ) {
2643     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2644     if ( !n ) return 0;
2645     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2646   }
2647
2648   gp_Vec V1(P[0], P[1]);
2649   gp_Vec V2(P[0], P[2]);
2650   gp_Vec V3(P[0], P[3]);
2651
2652   gp_Vec Cross1 = V1 ^ V2;
2653   gp_Vec Cross2 = V2 ^ V3;
2654
2655   i = 0;
2656   if (Cross1.Dot(Cross2) < 0)
2657   {
2658     Cross1 = V2 ^ V1;
2659     Cross2 = V1 ^ V3;
2660
2661     if (Cross1.Dot(Cross2) < 0)
2662       i = 2;
2663     else
2664       i = 1;
2665     swap ( i, i + 1, idNodes, P );
2666
2667     //     for ( int ii = 0; ii < 4; ii++ ) {
2668     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2669     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2670     //     }
2671   }
2672   return i;
2673 }
2674
2675 //=======================================================================
2676 //function : SortHexaNodes
2677 //purpose  : Set 8 nodes of a hexahedron in a good order.
2678 //           Return success status
2679 //=======================================================================
2680
2681 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2682                                       int               idNodes[] )
2683 {
2684   gp_Pnt P[8];
2685   int i;
2686   DUMPSO( "INPUT: ========================================");
2687   for ( i = 0; i < 8; i++ ) {
2688     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2689     if ( !n ) return false;
2690     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2691     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2692   }
2693   DUMPSO( "========================================");
2694
2695
2696   set<int> faceNodes;  // ids of bottom face nodes, to be found
2697   set<int> checkedId1; // ids of tried 2-nd nodes
2698   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2699   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2700   int iMin, iLoop1 = 0;
2701
2702   // Loop to try the 2-nd nodes
2703
2704   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2705   {
2706     // Find not checked 2-nd node
2707     for ( i = 1; i < 8; i++ )
2708       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2709         int id1 = idNodes[i];
2710         swap ( 1, i, idNodes, P );
2711         checkedId1.insert ( id1 );
2712         break;
2713       }
2714
2715     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2716     // ie that all but meybe one (id3 which is on the same face) nodes
2717     // lay on the same side from the triangle plane.
2718
2719     bool manyInPlane = false; // more than 4 nodes lay in plane
2720     int iLoop2 = 0;
2721     while ( ++iLoop2 < 6 ) {
2722
2723       // get 1-2-3 plane coeffs
2724       Standard_Real A, B, C, D;
2725       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2726       if ( N.SquareMagnitude() > gp::Resolution() )
2727       {
2728         gp_Pln pln ( P[0], N );
2729         pln.Coefficients( A, B, C, D );
2730
2731         // find the node (iMin) closest to pln
2732         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2733         set<int> idInPln;
2734         for ( i = 3; i < 8; i++ ) {
2735           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2736           if ( fabs( dist[i] ) < minDist ) {
2737             minDist = fabs( dist[i] );
2738             iMin = i;
2739           }
2740           if ( fabs( dist[i] ) <= tol )
2741             idInPln.insert( idNodes[i] );
2742         }
2743
2744         // there should not be more than 4 nodes in bottom plane
2745         if ( idInPln.size() > 1 )
2746         {
2747           DUMPSO( "### idInPln.size() = " << idInPln.size());
2748           // idInPlane does not contain the first 3 nodes
2749           if ( manyInPlane || idInPln.size() == 5)
2750             return false; // all nodes in one plane
2751           manyInPlane = true;
2752
2753           // set the 1-st node to be not in plane
2754           for ( i = 3; i < 8; i++ ) {
2755             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2756               DUMPSO( "### Reset 0-th node");
2757               swap( 0, i, idNodes, P );
2758               break;
2759             }
2760           }
2761
2762           // reset to re-check second nodes
2763           leastDist = DBL_MAX;
2764           faceNodes.clear();
2765           checkedId1.clear();
2766           iLoop1 = 0;
2767           break; // from iLoop2;
2768         }
2769
2770         // check that the other 4 nodes are on the same side
2771         bool sameSide = true;
2772         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2773         for ( i = 3; sameSide && i < 8; i++ ) {
2774           if ( i != iMin )
2775             sameSide = ( isNeg == dist[i] <= 0.);
2776         }
2777
2778         // keep best solution
2779         if ( sameSide && minDist < leastDist ) {
2780           leastDist = minDist;
2781           faceNodes.clear();
2782           faceNodes.insert( idNodes[ 1 ] );
2783           faceNodes.insert( idNodes[ 2 ] );
2784           faceNodes.insert( idNodes[ iMin ] );
2785           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2786                   << " leastDist = " << leastDist);
2787           if ( leastDist <= DBL_MIN )
2788             break;
2789         }
2790       }
2791
2792       // set next 3-d node to check
2793       int iNext = 2 + iLoop2;
2794       if ( iNext < 8 ) {
2795         DUMPSO( "Try 2-nd");
2796         swap ( 2, iNext, idNodes, P );
2797       }
2798     } // while ( iLoop2 < 6 )
2799   } // iLoop1
2800
2801   if ( faceNodes.empty() ) return false;
2802
2803   // Put the faceNodes in proper places
2804   for ( i = 4; i < 8; i++ ) {
2805     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2806       // find a place to put
2807       int iTo = 1;
2808       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2809         iTo++;
2810       DUMPSO( "Set faceNodes");
2811       swap ( iTo, i, idNodes, P );
2812     }
2813   }
2814
2815
2816   // Set nodes of the found bottom face in good order
2817   DUMPSO( " Found bottom face: ");
2818   i = SortQuadNodes( theMesh, idNodes );
2819   if ( i ) {
2820     gp_Pnt Ptmp = P[ i ];
2821     P[ i ] = P[ i+1 ];
2822     P[ i+1 ] = Ptmp;
2823   }
2824   //   else
2825   //     for ( int ii = 0; ii < 4; ii++ ) {
2826   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2827   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2828   //    }
2829
2830   // Gravity center of the top and bottom faces
2831   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2832   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2833
2834   // Get direction from the bottom to the top face
2835   gp_Vec upDir ( aGCb, aGCt );
2836   Standard_Real upDirSize = upDir.Magnitude();
2837   if ( upDirSize <= gp::Resolution() ) return false;
2838   upDir / upDirSize;
2839
2840   // Assure that the bottom face normal points up
2841   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2842   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2843   if ( Nb.Dot( upDir ) < 0 ) {
2844     DUMPSO( "Reverse bottom face");
2845     swap( 1, 3, idNodes, P );
2846   }
2847
2848   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2849   Standard_Real minDist = DBL_MAX;
2850   for ( i = 4; i < 8; i++ ) {
2851     // projection of P[i] to the plane defined by P[0] and upDir
2852     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2853     Standard_Real sqDist = P[0].SquareDistance( Pp );
2854     if ( sqDist < minDist ) {
2855       minDist = sqDist;
2856       iMin = i;
2857     }
2858   }
2859   DUMPSO( "Set 4-th");
2860   swap ( 4, iMin, idNodes, P );
2861
2862   // Set nodes of the top face in good order
2863   DUMPSO( "Sort top face");
2864   i = SortQuadNodes( theMesh, &idNodes[4] );
2865   if ( i ) {
2866     i += 4;
2867     gp_Pnt Ptmp = P[ i ];
2868     P[ i ] = P[ i+1 ];
2869     P[ i+1 ] = Ptmp;
2870   }
2871
2872   // Assure that direction of the top face normal is from the bottom face
2873   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2874   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2875   if ( Nt.Dot( upDir ) < 0 ) {
2876     DUMPSO( "Reverse top face");
2877     swap( 5, 7, idNodes, P );
2878   }
2879
2880   //   DUMPSO( "OUTPUT: ========================================");
2881   //   for ( i = 0; i < 8; i++ ) {
2882   //     float *p = ugrid->GetPoint(idNodes[i]);
2883   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2884   //   }
2885
2886   return true;
2887 }*/
2888
2889 //================================================================================
2890 /*!
2891  * \brief Return nodes linked to the given one
2892  * \param theNode - the node
2893  * \param linkedNodes - the found nodes
2894  * \param type - the type of elements to check
2895  *
2896  * Medium nodes are ignored
2897  */
2898 //================================================================================
2899
2900 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2901                                        TIDSortedElemSet &   linkedNodes,
2902                                        SMDSAbs_ElementType  type )
2903 {
2904   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2905   while ( elemIt->more() )
2906   {
2907     const SMDS_MeshElement* elem = elemIt->next();
2908     if(elem->GetType() == SMDSAbs_0DElement)
2909       continue;
2910
2911     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2912     if ( elem->GetType() == SMDSAbs_Volume )
2913     {
2914       SMDS_VolumeTool vol( elem );
2915       while ( nodeIt->more() ) {
2916         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2917         if ( theNode != n && vol.IsLinked( theNode, n ))
2918           linkedNodes.insert( n );
2919       }
2920     }
2921     else
2922     {
2923       for ( int i = 0; nodeIt->more(); ++i ) {
2924         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2925         if ( n == theNode ) {
2926           int iBefore = i - 1;
2927           int iAfter  = i + 1;
2928           if ( elem->IsQuadratic() ) {
2929             int nb = elem->NbNodes() / 2;
2930             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2931             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2932           }
2933           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2934           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2935         }
2936       }
2937     }
2938   }
2939 }
2940
2941 //=======================================================================
2942 //function : laplacianSmooth
2943 //purpose  : pulls theNode toward the center of surrounding nodes directly
2944 //           connected to that node along an element edge
2945 //=======================================================================
2946
2947 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2948                      const Handle(Geom_Surface)&          theSurface,
2949                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2950 {
2951   // find surrounding nodes
2952
2953   TIDSortedElemSet nodeSet;
2954   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2955
2956   // compute new coodrs
2957
2958   double coord[] = { 0., 0., 0. };
2959   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2960   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2961     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2962     if ( theSurface.IsNull() ) { // smooth in 3D
2963       coord[0] += node->X();
2964       coord[1] += node->Y();
2965       coord[2] += node->Z();
2966     }
2967     else { // smooth in 2D
2968       ASSERT( theUVMap.find( node ) != theUVMap.end() );
2969       gp_XY* uv = theUVMap[ node ];
2970       coord[0] += uv->X();
2971       coord[1] += uv->Y();
2972     }
2973   }
2974   int nbNodes = nodeSet.size();
2975   if ( !nbNodes )
2976     return;
2977   coord[0] /= nbNodes;
2978   coord[1] /= nbNodes;
2979
2980   if ( !theSurface.IsNull() ) {
2981     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2982     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2983     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2984     coord[0] = p3d.X();
2985     coord[1] = p3d.Y();
2986     coord[2] = p3d.Z();
2987   }
2988   else
2989     coord[2] /= nbNodes;
2990
2991   // move node
2992
2993   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2994 }
2995
2996 //=======================================================================
2997 //function : centroidalSmooth
2998 //purpose  : pulls theNode toward the element-area-weighted centroid of the
2999 //           surrounding elements
3000 //=======================================================================
3001
3002 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3003                       const Handle(Geom_Surface)&          theSurface,
3004                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3005 {
3006   gp_XYZ aNewXYZ(0.,0.,0.);
3007   SMESH::Controls::Area anAreaFunc;
3008   double totalArea = 0.;
3009   int nbElems = 0;
3010
3011   // compute new XYZ
3012
3013   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3014   while ( elemIt->more() )
3015   {
3016     const SMDS_MeshElement* elem = elemIt->next();
3017     nbElems++;
3018
3019     gp_XYZ elemCenter(0.,0.,0.);
3020     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3021     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3022     int nn = elem->NbNodes();
3023     if(elem->IsQuadratic()) nn = nn/2;
3024     int i=0;
3025     //while ( itN->more() ) {
3026     while ( i<nn ) {
3027       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3028       i++;
3029       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3030       aNodePoints.push_back( aP );
3031       if ( !theSurface.IsNull() ) { // smooth in 2D
3032         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3033         gp_XY* uv = theUVMap[ aNode ];
3034         aP.SetCoord( uv->X(), uv->Y(), 0. );
3035       }
3036       elemCenter += aP;
3037     }
3038     double elemArea = anAreaFunc.GetValue( aNodePoints );
3039     totalArea += elemArea;
3040     elemCenter /= nn;
3041     aNewXYZ += elemCenter * elemArea;
3042   }
3043   aNewXYZ /= totalArea;
3044   if ( !theSurface.IsNull() ) {
3045     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3046     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3047   }
3048
3049   // move node
3050
3051   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3052 }
3053
3054 //=======================================================================
3055 //function : getClosestUV
3056 //purpose  : return UV of closest projection
3057 //=======================================================================
3058
3059 static bool getClosestUV (Extrema_GenExtPS& projector,
3060                           const gp_Pnt&     point,
3061                           gp_XY &           result)
3062 {
3063   projector.Perform( point );
3064   if ( projector.IsDone() ) {
3065     double u, v, minVal = DBL_MAX;
3066     for ( int i = projector.NbExt(); i > 0; i-- )
3067 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3068       if ( projector.SquareDistance( i ) < minVal ) {
3069         minVal = projector.SquareDistance( i );
3070 #else
3071       if ( projector.Value( i ) < minVal ) {
3072         minVal = projector.Value( i );
3073 #endif
3074         projector.Point( i ).Parameter( u, v );
3075       }
3076     result.SetCoord( u, v );
3077     return true;
3078   }
3079   return false;
3080 }
3081
3082 //=======================================================================
3083 //function : Smooth
3084 //purpose  : Smooth theElements during theNbIterations or until a worst
3085 //           element has aspect ratio <= theTgtAspectRatio.
3086 //           Aspect Ratio varies in range [1.0, inf].
3087 //           If theElements is empty, the whole mesh is smoothed.
3088 //           theFixedNodes contains additionally fixed nodes. Nodes built
3089 //           on edges and boundary nodes are always fixed.
3090 //=======================================================================
3091
3092 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3093                                set<const SMDS_MeshNode*> & theFixedNodes,
3094                                const SmoothMethod          theSmoothMethod,
3095                                const int                   theNbIterations,
3096                                double                      theTgtAspectRatio,
3097                                const bool                  the2D)
3098 {
3099   myLastCreatedElems.Clear();
3100   myLastCreatedNodes.Clear();
3101
3102   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3103
3104   if ( theTgtAspectRatio < 1.0 )
3105     theTgtAspectRatio = 1.0;
3106
3107   const double disttol = 1.e-16;
3108
3109   SMESH::Controls::AspectRatio aQualityFunc;
3110
3111   SMESHDS_Mesh* aMesh = GetMeshDS();
3112
3113   if ( theElems.empty() ) {
3114     // add all faces to theElems
3115     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3116     while ( fIt->more() ) {
3117       const SMDS_MeshElement* face = fIt->next();
3118       theElems.insert( theElems.end(), face );
3119     }
3120   }
3121   // get all face ids theElems are on
3122   set< int > faceIdSet;
3123   TIDSortedElemSet::iterator itElem;
3124   if ( the2D )
3125     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3126       int fId = FindShape( *itElem );
3127       // check that corresponding submesh exists and a shape is face
3128       if (fId &&
3129           faceIdSet.find( fId ) == faceIdSet.end() &&
3130           aMesh->MeshElements( fId )) {
3131         TopoDS_Shape F = aMesh->IndexToShape( fId );
3132         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3133           faceIdSet.insert( fId );
3134       }
3135     }
3136   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3137
3138   // ===============================================
3139   // smooth elements on each TopoDS_Face separately
3140   // ===============================================
3141
3142   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3143   for ( ; fId != faceIdSet.rend(); ++fId ) {
3144     // get face surface and submesh
3145     Handle(Geom_Surface) surface;
3146     SMESHDS_SubMesh* faceSubMesh = 0;
3147     TopoDS_Face face;
3148     double fToler2 = 0, f,l;
3149     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3150     bool isUPeriodic = false, isVPeriodic = false;
3151     if ( *fId ) {
3152       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3153       surface = BRep_Tool::Surface( face );
3154       faceSubMesh = aMesh->MeshElements( *fId );
3155       fToler2 = BRep_Tool::Tolerance( face );
3156       fToler2 *= fToler2 * 10.;
3157       isUPeriodic = surface->IsUPeriodic();
3158       if ( isUPeriodic )
3159         surface->UPeriod();
3160       isVPeriodic = surface->IsVPeriodic();
3161       if ( isVPeriodic )
3162         surface->VPeriod();
3163       surface->Bounds( u1, u2, v1, v2 );
3164     }
3165     // ---------------------------------------------------------
3166     // for elements on a face, find movable and fixed nodes and
3167     // compute UV for them
3168     // ---------------------------------------------------------
3169     bool checkBoundaryNodes = false;
3170     bool isQuadratic = false;
3171     set<const SMDS_MeshNode*> setMovableNodes;
3172     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3173     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3174     list< const SMDS_MeshElement* > elemsOnFace;
3175
3176     Extrema_GenExtPS projector;
3177     GeomAdaptor_Surface surfAdaptor;
3178     if ( !surface.IsNull() ) {
3179       surfAdaptor.Load( surface );
3180       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3181     }
3182     int nbElemOnFace = 0;
3183     itElem = theElems.begin();
3184     // loop on not yet smoothed elements: look for elems on a face
3185     while ( itElem != theElems.end() ) {
3186       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3187         break; // all elements found
3188
3189       const SMDS_MeshElement* elem = *itElem;
3190       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3191            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3192         ++itElem;
3193         continue;
3194       }
3195       elemsOnFace.push_back( elem );
3196       theElems.erase( itElem++ );
3197       nbElemOnFace++;
3198
3199       if ( !isQuadratic )
3200         isQuadratic = elem->IsQuadratic();
3201
3202       // get movable nodes of elem
3203       const SMDS_MeshNode* node;
3204       SMDS_TypeOfPosition posType;
3205       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3206       int nn = 0, nbn =  elem->NbNodes();
3207       if(elem->IsQuadratic())
3208         nbn = nbn/2;
3209       while ( nn++ < nbn ) {
3210         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3211         const SMDS_PositionPtr& pos = node->GetPosition();
3212         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3213         if (posType != SMDS_TOP_EDGE &&
3214             posType != SMDS_TOP_VERTEX &&
3215             theFixedNodes.find( node ) == theFixedNodes.end())
3216         {
3217           // check if all faces around the node are on faceSubMesh
3218           // because a node on edge may be bound to face
3219           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3220           bool all = true;
3221           if ( faceSubMesh ) {
3222             while ( eIt->more() && all ) {
3223               const SMDS_MeshElement* e = eIt->next();
3224               all = faceSubMesh->Contains( e );
3225             }
3226           }
3227           if ( all )
3228             setMovableNodes.insert( node );
3229           else
3230             checkBoundaryNodes = true;
3231         }
3232         if ( posType == SMDS_TOP_3DSPACE )
3233           checkBoundaryNodes = true;
3234       }
3235
3236       if ( surface.IsNull() )
3237         continue;
3238
3239       // get nodes to check UV
3240       list< const SMDS_MeshNode* > uvCheckNodes;
3241       itN = elem->nodesIterator();
3242       nn = 0; nbn =  elem->NbNodes();
3243       if(elem->IsQuadratic())
3244         nbn = nbn/2;
3245       while ( nn++ < nbn ) {
3246         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3247         if ( uvMap.find( node ) == uvMap.end() )
3248           uvCheckNodes.push_back( node );
3249         // add nodes of elems sharing node
3250         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3251         //         while ( eIt->more() ) {
3252         //           const SMDS_MeshElement* e = eIt->next();
3253         //           if ( e != elem ) {
3254         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3255         //             while ( nIt->more() ) {
3256         //               const SMDS_MeshNode* n =
3257         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3258         //               if ( uvMap.find( n ) == uvMap.end() )
3259         //                 uvCheckNodes.push_back( n );
3260         //             }
3261         //           }
3262         //         }
3263       }
3264       // check UV on face
3265       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3266       for ( ; n != uvCheckNodes.end(); ++n ) {
3267         node = *n;
3268         gp_XY uv( 0, 0 );
3269         const SMDS_PositionPtr& pos = node->GetPosition();
3270         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3271         // get existing UV
3272         switch ( posType ) {
3273         case SMDS_TOP_FACE: {
3274           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3275           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3276           break;
3277         }
3278         case SMDS_TOP_EDGE: {
3279           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3280           Handle(Geom2d_Curve) pcurve;
3281           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3282             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3283           if ( !pcurve.IsNull() ) {
3284             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3285             uv = pcurve->Value( u ).XY();
3286           }
3287           break;
3288         }
3289         case SMDS_TOP_VERTEX: {
3290           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3291           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3292             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3293           break;
3294         }
3295         default:;
3296         }
3297         // check existing UV
3298         bool project = true;
3299         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3300         double dist1 = DBL_MAX, dist2 = 0;
3301         if ( posType != SMDS_TOP_3DSPACE ) {
3302           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3303           project = dist1 > fToler2;
3304         }
3305         if ( project ) { // compute new UV
3306           gp_XY newUV;
3307           if ( !getClosestUV( projector, pNode, newUV )) {
3308             MESSAGE("Node Projection Failed " << node);
3309           }
3310           else {
3311             if ( isUPeriodic )
3312               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3313             if ( isVPeriodic )
3314               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3315             // check new UV
3316             if ( posType != SMDS_TOP_3DSPACE )
3317               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3318             if ( dist2 < dist1 )
3319               uv = newUV;
3320           }
3321         }
3322         // store UV in the map
3323         listUV.push_back( uv );
3324         uvMap.insert( make_pair( node, &listUV.back() ));
3325       }
3326     } // loop on not yet smoothed elements
3327
3328     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3329       checkBoundaryNodes = true;
3330
3331     // fix nodes on mesh boundary
3332
3333     if ( checkBoundaryNodes ) {
3334       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3335       map< SMESH_TLink, int >::iterator link_nb;
3336       // put all elements links to linkNbMap
3337       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3338       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3339         const SMDS_MeshElement* elem = (*elemIt);
3340         int nbn =  elem->NbCornerNodes();
3341         // loop on elem links: insert them in linkNbMap
3342         for ( int iN = 0; iN < nbn; ++iN ) {
3343           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3344           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3345           SMESH_TLink link( n1, n2 );
3346           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3347           link_nb->second++;
3348         }
3349       }
3350       // remove nodes that are in links encountered only once from setMovableNodes
3351       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3352         if ( link_nb->second == 1 ) {
3353           setMovableNodes.erase( link_nb->first.node1() );
3354           setMovableNodes.erase( link_nb->first.node2() );
3355         }
3356       }
3357     }
3358
3359     // -----------------------------------------------------
3360     // for nodes on seam edge, compute one more UV ( uvMap2 );
3361     // find movable nodes linked to nodes on seam and which
3362     // are to be smoothed using the second UV ( uvMap2 )
3363     // -----------------------------------------------------
3364
3365     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3366     if ( !surface.IsNull() ) {
3367       TopExp_Explorer eExp( face, TopAbs_EDGE );
3368       for ( ; eExp.More(); eExp.Next() ) {
3369         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3370         if ( !BRep_Tool::IsClosed( edge, face ))
3371           continue;
3372         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3373         if ( !sm ) continue;
3374         // find out which parameter varies for a node on seam
3375         double f,l;
3376         gp_Pnt2d uv1, uv2;
3377         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3378         if ( pcurve.IsNull() ) continue;
3379         uv1 = pcurve->Value( f );
3380         edge.Reverse();
3381         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3382         if ( pcurve.IsNull() ) continue;
3383         uv2 = pcurve->Value( f );
3384         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3385         // assure uv1 < uv2
3386         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3387           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3388         }
3389         // get nodes on seam and its vertices
3390         list< const SMDS_MeshNode* > seamNodes;
3391         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3392         while ( nSeamIt->more() ) {
3393           const SMDS_MeshNode* node = nSeamIt->next();
3394           if ( !isQuadratic || !IsMedium( node ))
3395             seamNodes.push_back( node );
3396         }
3397         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3398         for ( ; vExp.More(); vExp.Next() ) {
3399           sm = aMesh->MeshElements( vExp.Current() );
3400           if ( sm ) {
3401             nSeamIt = sm->GetNodes();
3402             while ( nSeamIt->more() )
3403               seamNodes.push_back( nSeamIt->next() );
3404           }
3405         }
3406         // loop on nodes on seam
3407         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3408         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3409           const SMDS_MeshNode* nSeam = *noSeIt;
3410           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3411           if ( n_uv == uvMap.end() )
3412             continue;
3413           // set the first UV
3414           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3415           // set the second UV
3416           listUV.push_back( *n_uv->second );
3417           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3418           if ( uvMap2.empty() )
3419             uvMap2 = uvMap; // copy the uvMap contents
3420           uvMap2[ nSeam ] = &listUV.back();
3421
3422           // collect movable nodes linked to ones on seam in nodesNearSeam
3423           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3424           while ( eIt->more() ) {
3425             const SMDS_MeshElement* e = eIt->next();
3426             int nbUseMap1 = 0, nbUseMap2 = 0;
3427             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3428             int nn = 0, nbn =  e->NbNodes();
3429             if(e->IsQuadratic()) nbn = nbn/2;
3430             while ( nn++ < nbn )
3431             {
3432               const SMDS_MeshNode* n =
3433                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3434               if (n == nSeam ||
3435                   setMovableNodes.find( n ) == setMovableNodes.end() )
3436                 continue;
3437               // add only nodes being closer to uv2 than to uv1
3438               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3439                            0.5 * ( n->Y() + nSeam->Y() ),
3440                            0.5 * ( n->Z() + nSeam->Z() ));
3441               gp_XY uv;
3442               getClosestUV( projector, pMid, uv );
3443               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3444                 nodesNearSeam.insert( n );
3445                 nbUseMap2++;
3446               }
3447               else
3448                 nbUseMap1++;
3449             }
3450             // for centroidalSmooth all element nodes must
3451             // be on one side of a seam
3452             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3453               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3454               nn = 0;
3455               while ( nn++ < nbn ) {
3456                 const SMDS_MeshNode* n =
3457                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3458                 setMovableNodes.erase( n );
3459               }
3460             }
3461           }
3462         } // loop on nodes on seam
3463       } // loop on edge of a face
3464     } // if ( !face.IsNull() )
3465
3466     if ( setMovableNodes.empty() ) {
3467       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3468       continue; // goto next face
3469     }
3470
3471     // -------------
3472     // SMOOTHING //
3473     // -------------
3474
3475     int it = -1;
3476     double maxRatio = -1., maxDisplacement = -1.;
3477     set<const SMDS_MeshNode*>::iterator nodeToMove;
3478     for ( it = 0; it < theNbIterations; it++ ) {
3479       maxDisplacement = 0.;
3480       nodeToMove = setMovableNodes.begin();
3481       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3482         const SMDS_MeshNode* node = (*nodeToMove);
3483         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3484
3485         // smooth
3486         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3487         if ( theSmoothMethod == LAPLACIAN )
3488           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3489         else
3490           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3491
3492         // node displacement
3493         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3494         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3495         if ( aDispl > maxDisplacement )
3496           maxDisplacement = aDispl;
3497       }
3498       // no node movement => exit
3499       //if ( maxDisplacement < 1.e-16 ) {
3500       if ( maxDisplacement < disttol ) {
3501         MESSAGE("-- no node movement --");
3502         break;
3503       }
3504
3505       // check elements quality
3506       maxRatio  = 0;
3507       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3508       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3509         const SMDS_MeshElement* elem = (*elemIt);
3510         if ( !elem || elem->GetType() != SMDSAbs_Face )
3511           continue;
3512         SMESH::Controls::TSequenceOfXYZ aPoints;
3513         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3514           double aValue = aQualityFunc.GetValue( aPoints );
3515           if ( aValue > maxRatio )
3516             maxRatio = aValue;
3517         }
3518       }
3519       if ( maxRatio <= theTgtAspectRatio ) {
3520         MESSAGE("-- quality achived --");
3521         break;
3522       }
3523       if (it+1 == theNbIterations) {
3524         MESSAGE("-- Iteration limit exceeded --");
3525       }
3526     } // smoothing iterations
3527
3528     MESSAGE(" Face id: " << *fId <<
3529             " Nb iterstions: " << it <<
3530             " Displacement: " << maxDisplacement <<
3531             " Aspect Ratio " << maxRatio);
3532
3533     // ---------------------------------------
3534     // new nodes positions are computed,
3535     // record movement in DS and set new UV
3536     // ---------------------------------------
3537     nodeToMove = setMovableNodes.begin();
3538     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3539       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3540       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3541       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3542       if ( node_uv != uvMap.end() ) {
3543         gp_XY* uv = node_uv->second;
3544         node->SetPosition
3545           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3546       }
3547     }
3548
3549     // move medium nodes of quadratic elements
3550     if ( isQuadratic )
3551     {
3552       SMESH_MesherHelper helper( *GetMesh() );
3553       if ( !face.IsNull() )
3554         helper.SetSubShape( face );
3555       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3556       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3557         const SMDS_VtkFace* QF =
3558           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3559         if(QF && QF->IsQuadratic()) {
3560           vector<const SMDS_MeshNode*> Ns;
3561           Ns.reserve(QF->NbNodes()+1);
3562           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3563           while ( anIter->more() )
3564             Ns.push_back( cast2Node(anIter->next()) );
3565           Ns.push_back( Ns[0] );
3566           double x, y, z;
3567           for(int i=0; i<QF->NbNodes(); i=i+2) {
3568             if ( !surface.IsNull() ) {
3569               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3570               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3571               gp_XY uv = ( uv1 + uv2 ) / 2.;
3572               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3573               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3574             }
3575             else {
3576               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3577               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3578               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3579             }
3580             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3581                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3582                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3583               // we have to move i+1 node
3584               aMesh->MoveNode( Ns[i+1], x, y, z );
3585             }
3586           }
3587         }
3588       }
3589     }
3590
3591   } // loop on face ids
3592
3593 }
3594
3595 //=======================================================================
3596 //function : isReverse
3597 //purpose  : Return true if normal of prevNodes is not co-directied with
3598 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3599 //           iNotSame is where prevNodes and nextNodes are different.
3600 //           If result is true then future volume orientation is OK
3601 //=======================================================================
3602
3603 static bool isReverse(const SMDS_MeshElement*             face,
3604                       const vector<const SMDS_MeshNode*>& prevNodes,
3605                       const vector<const SMDS_MeshNode*>& nextNodes,
3606                       const int                           iNotSame)
3607 {
3608
3609   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3610   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3611   gp_XYZ extrDir( pN - pP ), faceNorm;
3612   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3613
3614   return faceNorm * extrDir < 0.0;
3615 }
3616
3617 //=======================================================================
3618 /*!
3619  * \brief Create elements by sweeping an element
3620  * \param elem - element to sweep
3621  * \param newNodesItVec - nodes generated from each node of the element
3622  * \param newElems - generated elements
3623  * \param nbSteps - number of sweeping steps
3624  * \param srcElements - to append elem for each generated element
3625  */
3626 //=======================================================================
3627
3628 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3629                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3630                                     list<const SMDS_MeshElement*>&        newElems,
3631                                     const int                             nbSteps,
3632                                     SMESH_SequenceOfElemPtr&              srcElements)
3633 {
3634   //MESSAGE("sweepElement " << nbSteps);
3635   SMESHDS_Mesh* aMesh = GetMeshDS();
3636
3637   const int           nbNodes = elem->NbNodes();
3638   const int         nbCorners = elem->NbCornerNodes();
3639   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3640                                                           polyhedron creation !!! */
3641   // Loop on elem nodes:
3642   // find new nodes and detect same nodes indices
3643   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3644   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3645   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3646   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3647
3648   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3649   vector<int> sames(nbNodes);
3650   vector<bool> isSingleNode(nbNodes);
3651
3652   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3653     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3654     const SMDS_MeshNode*                         node = nnIt->first;
3655     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3656     if ( listNewNodes.empty() )
3657       return;
3658
3659     itNN   [ iNode ] = listNewNodes.begin();
3660     prevNod[ iNode ] = node;
3661     nextNod[ iNode ] = listNewNodes.front();
3662
3663     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3664                                                              corner node of linear */
3665     if ( prevNod[ iNode ] != nextNod [ iNode ])
3666       nbDouble += !isSingleNode[iNode];
3667
3668     if( iNode < nbCorners ) { // check corners only
3669       if ( prevNod[ iNode ] == nextNod [ iNode ])
3670         sames[nbSame++] = iNode;
3671       else
3672         iNotSameNode = iNode;
3673     }
3674   }
3675
3676   if ( nbSame == nbNodes || nbSame > 2) {
3677     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3678     return;
3679   }
3680
3681   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3682   {
3683     // fix nodes order to have bottom normal external
3684     if ( baseType == SMDSEntity_Polygon )
3685     {
3686       std::reverse( itNN.begin(), itNN.end() );
3687       std::reverse( prevNod.begin(), prevNod.end() );
3688       std::reverse( midlNod.begin(), midlNod.end() );
3689       std::reverse( nextNod.begin(), nextNod.end() );
3690       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3691     }
3692     else
3693     {
3694       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3695       SMDS_MeshCell::applyInterlace( ind, itNN );
3696       SMDS_MeshCell::applyInterlace( ind, prevNod );
3697       SMDS_MeshCell::applyInterlace( ind, nextNod );
3698       SMDS_MeshCell::applyInterlace( ind, midlNod );
3699       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3700       if ( nbSame > 0 )
3701       {
3702         sames[nbSame] = iNotSameNode;
3703         for ( int j = 0; j <= nbSame; ++j )
3704           for ( size_t i = 0; i < ind.size(); ++i )
3705             if ( ind[i] == sames[j] )
3706             {
3707               sames[j] = i;
3708               break;
3709             }
3710         iNotSameNode = sames[nbSame];
3711       }
3712     }
3713   }
3714
3715   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3716   if ( nbSame > 0 ) {
3717     iSameNode    = sames[ nbSame-1 ];
3718     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3719     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3720     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3721   }
3722
3723   // make new elements
3724   for (int iStep = 0; iStep < nbSteps; iStep++ )
3725   {
3726     // get next nodes
3727     for ( iNode = 0; iNode < nbNodes; iNode++ )
3728     {
3729       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3730       nextNod[ iNode ] = *itNN[ iNode ]++;
3731     }
3732
3733     SMDS_MeshElement* aNewElem = 0;
3734     /*if(!elem->IsPoly())*/ {
3735       switch ( baseType ) {
3736       case SMDSEntity_0D:
3737       case SMDSEntity_Node: { // sweep NODE
3738         if ( nbSame == 0 ) {
3739           if ( isSingleNode[0] )
3740             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3741           else
3742             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3743         }
3744         else
3745           return;
3746         break;
3747       }
3748       case SMDSEntity_Edge: { // sweep EDGE
3749         if ( nbDouble == 0 )
3750         {
3751           if ( nbSame == 0 ) // ---> quadrangle
3752             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3753                                       nextNod[ 1 ], nextNod[ 0 ] );
3754           else               // ---> triangle
3755             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3756                                       nextNod[ iNotSameNode ] );
3757         }
3758         else                 // ---> polygon
3759         {
3760           vector<const SMDS_MeshNode*> poly_nodes;
3761           poly_nodes.push_back( prevNod[0] );
3762           poly_nodes.push_back( prevNod[1] );
3763           if ( prevNod[1] != nextNod[1] )
3764           {
3765             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3766             poly_nodes.push_back( nextNod[1] );
3767           }
3768           if ( prevNod[0] != nextNod[0] )
3769           {
3770             poly_nodes.push_back( nextNod[0] );
3771             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3772           }
3773           switch ( poly_nodes.size() ) {
3774           case 3:
3775             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3776             break;
3777           case 4:
3778             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3779                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3780             break;
3781           default:
3782             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3783           }
3784         }
3785         break;
3786       }
3787       case SMDSEntity_Triangle: // TRIANGLE --->
3788         {
3789           if ( nbDouble > 0 ) break;
3790           if ( nbSame == 0 )       // ---> pentahedron
3791             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3792                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3793
3794           else if ( nbSame == 1 )  // ---> pyramid
3795             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3796                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3797                                          nextNod[ iSameNode ]);
3798
3799           else // 2 same nodes:       ---> tetrahedron
3800             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3801                                          nextNod[ iNotSameNode ]);
3802           break;
3803         }
3804       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3805         {
3806           if ( nbSame == 2 )
3807             return;
3808           if ( nbDouble+nbSame == 2 )
3809           {
3810             if(nbSame==0) {      // ---> quadratic quadrangle
3811               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3812                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3813             }
3814             else { //(nbSame==1) // ---> quadratic triangle
3815               if(sames[0]==2) {
3816                 return; // medium node on axis
3817               }
3818               else if(sames[0]==0)
3819                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3820                                           nextNod[2], midlNod[1], prevNod[2]);
3821               else // sames[0]==1
3822                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3823                                           midlNod[0], nextNod[2], prevNod[2]);
3824             }
3825           }
3826           else if ( nbDouble == 3 )
3827           {
3828             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3829               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3830                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3831             }
3832           }
3833           else
3834             return;
3835           break;
3836         }
3837       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3838         if ( nbDouble > 0 ) break;
3839
3840         if ( nbSame == 0 )       // ---> hexahedron
3841           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3842                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3843
3844         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3845           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3846                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3847                                        nextNod[ iSameNode ]);
3848           newElems.push_back( aNewElem );
3849           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3850                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3851                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3852         }
3853         else if ( nbSame == 2 ) { // ---> pentahedron
3854           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3855             // iBeforeSame is same too
3856             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3857                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3858                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3859           else
3860             // iAfterSame is same too
3861             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3862                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3863                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3864         }
3865         break;
3866       }
3867       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3868         if ( nbDouble+nbSame != 3 ) break;
3869         if(nbSame==0) {
3870           // --->  pentahedron with 15 nodes
3871           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3872                                        nextNod[0], nextNod[1], nextNod[2],
3873                                        prevNod[3], prevNod[4], prevNod[5],
3874                                        nextNod[3], nextNod[4], nextNod[5],
3875                                        midlNod[0], midlNod[1], midlNod[2]);
3876         }
3877         else if(nbSame==1) {
3878           // --->  2d order pyramid of 13 nodes
3879           int apex = iSameNode;
3880           int i0 = ( apex + 1 ) % nbCorners;
3881           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3882           int i0a = apex + 3;
3883           int i1a = i1 + 3;
3884           int i01 = i0 + 3;
3885           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3886                                       nextNod[i0], nextNod[i1], prevNod[apex],
3887                                       prevNod[i01], midlNod[i0],
3888                                       nextNod[i01], midlNod[i1],
3889                                       prevNod[i1a], prevNod[i0a],
3890                                       nextNod[i0a], nextNod[i1a]);
3891         }
3892         else if(nbSame==2) {
3893           // --->  2d order tetrahedron of 10 nodes
3894           int n1 = iNotSameNode;
3895           int n2 = ( n1 + 1             ) % nbCorners;
3896           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3897           int n12 = n1 + 3;
3898           int n23 = n2 + 3;
3899           int n31 = n3 + 3;
3900           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3901                                        prevNod[n12], prevNod[n23], prevNod[n31],
3902                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3903         }
3904         break;
3905       }
3906       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3907         if( nbSame == 0 ) {
3908           if ( nbDouble != 4 ) break;
3909           // --->  hexahedron with 20 nodes
3910           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3911                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3912                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3913                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3914                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3915         }
3916         else if(nbSame==1) {
3917           // ---> pyramid + pentahedron - can not be created since it is needed
3918           // additional middle node at the center of face
3919           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3920           return;
3921         }
3922         else if( nbSame == 2 ) {
3923           if ( nbDouble != 2 ) break;
3924           // --->  2d order Pentahedron with 15 nodes
3925           int n1,n2,n4,n5;
3926           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3927             // iBeforeSame is same too
3928             n1 = iBeforeSame;
3929             n2 = iOpposSame;
3930             n4 = iSameNode;
3931             n5 = iAfterSame;
3932           }
3933           else {
3934             // iAfterSame is same too
3935             n1 = iSameNode;
3936             n2 = iBeforeSame;
3937             n4 = iAfterSame;
3938             n5 = iOpposSame;
3939           }
3940           int n12 = n2 + 4;
3941           int n45 = n4 + 4;
3942           int n14 = n1 + 4;
3943           int n25 = n5 + 4;
3944           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3945                                        prevNod[n4], prevNod[n5], nextNod[n5],
3946                                        prevNod[n12], midlNod[n2], nextNod[n12],
3947                                        prevNod[n45], midlNod[n5], nextNod[n45],
3948                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3949         }
3950         break;
3951       }
3952       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3953
3954         if( nbSame == 0 && nbDouble == 9 ) {
3955           // --->  tri-quadratic hexahedron with 27 nodes
3956           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3957                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3958                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3959                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3960                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3961                                        prevNod[8], // bottom center
3962                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3963                                        nextNod[8], // top center
3964                                        midlNod[8]);// elem center
3965         }
3966         else
3967         {
3968           return;
3969         }
3970         break;
3971       }
3972       case SMDSEntity_Polygon: { // sweep POLYGON
3973
3974         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3975           // --->  hexagonal prism
3976           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3977                                        prevNod[3], prevNod[4], prevNod[5],
3978                                        nextNod[0], nextNod[1], nextNod[2],
3979                                        nextNod[3], nextNod[4], nextNod[5]);
3980         }
3981         break;
3982       }
3983       case SMDSEntity_Ball:
3984         return;
3985
3986       default:
3987         break;
3988       }
3989     }
3990
3991     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3992     {
3993       if ( baseType != SMDSEntity_Polygon )
3994       {
3995         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3996         SMDS_MeshCell::applyInterlace( ind, prevNod );
3997         SMDS_MeshCell::applyInterlace( ind, nextNod );
3998         SMDS_MeshCell::applyInterlace( ind, midlNod );
3999         SMDS_MeshCell::applyInterlace( ind, itNN );
4000         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4001         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4002       }
4003       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4004       vector<int> quantities (nbNodes + 2);
4005       polyedre_nodes.clear();
4006       quantities.clear();
4007
4008       // bottom of prism
4009       for (int inode = 0; inode < nbNodes; inode++)
4010         polyedre_nodes.push_back( prevNod[inode] );
4011       quantities.push_back( nbNodes );
4012
4013       // top of prism
4014       polyedre_nodes.push_back( nextNod[0] );
4015       for (int inode = nbNodes; inode-1; --inode )
4016         polyedre_nodes.push_back( nextNod[inode-1] );
4017       quantities.push_back( nbNodes );
4018
4019       // side faces
4020       for (int iface = 0; iface < nbNodes; iface++)
4021       {
4022         const int prevNbNodes = polyedre_nodes.size();
4023         int inextface = (iface+1) % nbNodes;
4024         polyedre_nodes.push_back( prevNod[inextface] );
4025         polyedre_nodes.push_back( prevNod[iface] );
4026         if ( prevNod[iface] != nextNod[iface] )
4027         {
4028           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4029           polyedre_nodes.push_back( nextNod[iface] );
4030         }
4031         if ( prevNod[inextface] != nextNod[inextface] )
4032         {
4033           polyedre_nodes.push_back( nextNod[inextface] );
4034           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4035         }
4036         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4037         if ( nbFaceNodes > 2 )
4038           quantities.push_back( nbFaceNodes );
4039         else // degenerated face
4040           polyedre_nodes.resize( prevNbNodes );
4041       }
4042       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4043     }
4044
4045     if ( aNewElem ) {
4046       newElems.push_back( aNewElem );
4047       myLastCreatedElems.Append(aNewElem);
4048       srcElements.Append( elem );
4049     }
4050
4051     // set new prev nodes
4052     for ( iNode = 0; iNode < nbNodes; iNode++ )
4053       prevNod[ iNode ] = nextNod[ iNode ];
4054
4055   } // for steps
4056 }
4057
4058 //=======================================================================
4059 /*!
4060  * \brief Create 1D and 2D elements around swept elements
4061  * \param mapNewNodes - source nodes and ones generated from them
4062  * \param newElemsMap - source elements and ones generated from them
4063  * \param elemNewNodesMap - nodes generated from each node of each element
4064  * \param elemSet - all swept elements
4065  * \param nbSteps - number of sweeping steps
4066  * \param srcElements - to append elem for each generated element
4067  */
4068 //=======================================================================
4069
4070 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4071                                   TElemOfElemListMap &     newElemsMap,
4072                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4073                                   TIDSortedElemSet&        elemSet,
4074                                   const int                nbSteps,
4075                                   SMESH_SequenceOfElemPtr& srcElements)
4076 {
4077   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4078   SMESHDS_Mesh* aMesh = GetMeshDS();
4079
4080   // Find nodes belonging to only one initial element - sweep them to get edges.
4081
4082   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4083   for ( ; nList != mapNewNodes.end(); nList++ )
4084   {
4085     const SMDS_MeshNode* node =
4086       static_cast<const SMDS_MeshNode*>( nList->first );
4087     if ( newElemsMap.count( node ))
4088       continue; // node was extruded into edge
4089     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4090     int nbInitElems = 0;
4091     const SMDS_MeshElement* el = 0;
4092     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4093     while ( eIt->more() && nbInitElems < 2 ) {
4094       el = eIt->next();
4095       SMDSAbs_ElementType type = el->GetType();
4096       if ( type == SMDSAbs_Volume || type < highType ) continue;
4097       if ( type > highType ) {
4098         nbInitElems = 0;
4099         highType = type;
4100       }
4101       nbInitElems += elemSet.count(el);
4102     }
4103     if ( nbInitElems < 2 ) {
4104       bool NotCreateEdge = el && el->IsMediumNode(node);
4105       if(!NotCreateEdge) {
4106         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4107         list<const SMDS_MeshElement*> newEdges;
4108         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4109       }
4110     }
4111   }
4112
4113   // Make a ceiling for each element ie an equal element of last new nodes.
4114   // Find free links of faces - make edges and sweep them into faces.
4115
4116   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4117   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4118   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4119   {
4120     const SMDS_MeshElement* elem = itElem->first;
4121     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4122
4123     if(itElem->second.size()==0) continue;
4124
4125     const bool isQuadratic = elem->IsQuadratic();
4126
4127     if ( elem->GetType() == SMDSAbs_Edge ) {
4128       // create a ceiling edge
4129       if ( !isQuadratic ) {
4130         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4131                                vecNewNodes[ 1 ]->second.back())) {
4132           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4133                                                    vecNewNodes[ 1 ]->second.back()));
4134           srcElements.Append( myLastCreatedElems.Last() );
4135         }
4136       }
4137       else {
4138         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4139                                vecNewNodes[ 1 ]->second.back(),
4140                                vecNewNodes[ 2 ]->second.back())) {
4141           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4142                                                    vecNewNodes[ 1 ]->second.back(),
4143                                                    vecNewNodes[ 2 ]->second.back()));
4144           srcElements.Append( myLastCreatedElems.Last() );
4145         }
4146       }
4147     }
4148     if ( elem->GetType() != SMDSAbs_Face )
4149       continue;
4150
4151     bool hasFreeLinks = false;
4152
4153     TIDSortedElemSet avoidSet;
4154     avoidSet.insert( elem );
4155
4156     set<const SMDS_MeshNode*> aFaceLastNodes;
4157     int iNode, nbNodes = vecNewNodes.size();
4158     if ( !isQuadratic ) {
4159       // loop on the face nodes
4160       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4161         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4162         // look for free links of the face
4163         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4164         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4165         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4166         // check if a link is free
4167         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4168           hasFreeLinks = true;
4169           // make an edge and a ceiling for a new edge
4170           if ( !aMesh->FindEdge( n1, n2 )) {
4171             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4172             srcElements.Append( myLastCreatedElems.Last() );
4173           }
4174           n1 = vecNewNodes[ iNode ]->second.back();
4175           n2 = vecNewNodes[ iNext ]->second.back();
4176           if ( !aMesh->FindEdge( n1, n2 )) {
4177             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4178             srcElements.Append( myLastCreatedElems.Last() );
4179           }
4180         }
4181       }
4182     }
4183     else { // elem is quadratic face
4184       int nbn = nbNodes/2;
4185       for ( iNode = 0; iNode < nbn; iNode++ ) {
4186         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4187         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4188         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4189         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4190         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4191         // check if a link is free
4192         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4193              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4194              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4195           hasFreeLinks = true;
4196           // make an edge and a ceiling for a new edge
4197           // find medium node
4198           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4199             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4200             srcElements.Append( myLastCreatedElems.Last() );
4201           }
4202           n1 = vecNewNodes[ iNode ]->second.back();
4203           n2 = vecNewNodes[ iNext ]->second.back();
4204           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4205           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4206             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4207             srcElements.Append( myLastCreatedElems.Last() );
4208           }
4209         }
4210       }
4211       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4212         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4213       }
4214     }
4215
4216     // sweep free links into faces
4217
4218     if ( hasFreeLinks )  {
4219       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4220       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4221
4222       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4223       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4224         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4225         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4226       }
4227       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4228         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4229         std::advance( v, volNb );
4230         // find indices of free faces of a volume and their source edges
4231         list< int > freeInd;
4232         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4233         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4234         int iF, nbF = vTool.NbFaces();
4235         for ( iF = 0; iF < nbF; iF ++ ) {
4236           if (vTool.IsFreeFace( iF ) &&
4237               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4238               initNodeSet != faceNodeSet) // except an initial face
4239           {
4240             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4241               continue;
4242             freeInd.push_back( iF );
4243             // find source edge of a free face iF
4244             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4245             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4246             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4247                                    initNodeSet.begin(), initNodeSet.end(),
4248                                    commonNodes.begin());
4249             if ( (*v)->IsQuadratic() )
4250               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4251             else
4252               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4253 #ifdef _DEBUG_
4254             if ( !srcEdges.back() )
4255             {
4256               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4257                    << iF << " of volume #" << vTool.ID() << endl;
4258             }
4259 #endif
4260           }
4261         }
4262         if ( freeInd.empty() )
4263           continue;
4264
4265         // create faces for all steps;
4266         // if such a face has been already created by sweep of edge,
4267         // assure that its orientation is OK
4268         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4269           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4270           vTool.SetExternalNormal();
4271           const int nextShift = vTool.IsForward() ? +1 : -1;
4272           list< int >::iterator ind = freeInd.begin();
4273           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4274           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4275           {
4276             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4277             int nbn = vTool.NbFaceNodes( *ind );
4278             const SMDS_MeshElement * f = 0;
4279             if ( nbn == 3 )              ///// triangle
4280             {
4281               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4282               if ( !f ||
4283                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4284               {
4285                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4286                                                      nodes[ 1 ],
4287                                                      nodes[ 1 + nextShift ] };
4288                 if ( f )
4289                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4290                 else
4291                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4292                                                             newOrder[ 2 ] ));
4293               }
4294             }
4295             else if ( nbn == 4 )       ///// quadrangle
4296             {
4297               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4298               if ( !f ||
4299                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4300               {
4301                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4302                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4303                 if ( f )
4304                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4305                 else
4306                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4307                                                             newOrder[ 2 ], newOrder[ 3 ]));
4308               }
4309             }
4310             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4311             {
4312               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4313               if ( !f ||
4314                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4315               {
4316                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4317                                                      nodes[2],
4318                                                      nodes[2 + 2*nextShift],
4319                                                      nodes[3 - 2*nextShift],
4320                                                      nodes[3],
4321                                                      nodes[3 + 2*nextShift]};
4322                 if ( f )
4323                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4324                 else
4325                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4326                                                             newOrder[ 1 ],
4327                                                             newOrder[ 2 ],
4328                                                             newOrder[ 3 ],
4329                                                             newOrder[ 4 ],
4330                                                             newOrder[ 5 ] ));
4331               }
4332             }
4333             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4334             {
4335               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4336                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4337               if ( !f ||
4338                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4339               {
4340                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4341                                                      nodes[4 - 2*nextShift],
4342                                                      nodes[4],
4343                                                      nodes[4 + 2*nextShift],
4344                                                      nodes[1],
4345                                                      nodes[5 - 2*nextShift],
4346                                                      nodes[5],
4347                                                      nodes[5 + 2*nextShift] };
4348                 if ( f )
4349                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4350                 else
4351                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4352                                                            newOrder[ 2 ], newOrder[ 3 ],
4353                                                            newOrder[ 4 ], newOrder[ 5 ],
4354                                                            newOrder[ 6 ], newOrder[ 7 ]));
4355               }
4356             }
4357             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4358             {
4359               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4360                                       SMDSAbs_Face, /*noMedium=*/false);
4361               if ( !f ||
4362                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4363               {
4364                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4365                                                      nodes[4 - 2*nextShift],
4366                                                      nodes[4],
4367                                                      nodes[4 + 2*nextShift],
4368                                                      nodes[1],
4369                                                      nodes[5 - 2*nextShift],
4370                                                      nodes[5],
4371                                                      nodes[5 + 2*nextShift],
4372                                                      nodes[8] };
4373                 if ( f )
4374                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4375                 else
4376                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4377                                                            newOrder[ 2 ], newOrder[ 3 ],
4378                                                            newOrder[ 4 ], newOrder[ 5 ],
4379                                                            newOrder[ 6 ], newOrder[ 7 ],
4380                                                            newOrder[ 8 ]));
4381               }
4382             }
4383             else  //////// polygon
4384             {
4385               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4386               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4387               if ( !f ||
4388                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4389               {
4390                 if ( !vTool.IsForward() )
4391                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4392                 if ( f )
4393                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4394                 else
4395                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4396               }
4397             }
4398
4399             while ( srcElements.Length() < myLastCreatedElems.Length() )
4400               srcElements.Append( *srcEdge );
4401
4402           }  // loop on free faces
4403
4404           // go to the next volume
4405           iVol = 0;
4406           while ( iVol++ < nbVolumesByStep ) v++;
4407
4408         } // loop on steps
4409       } // loop on volumes of one step
4410     } // sweep free links into faces
4411
4412     // Make a ceiling face with a normal external to a volume
4413
4414     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4415
4416     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4417     if ( iF >= 0 ) {
4418       lastVol.SetExternalNormal();
4419       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4420       int nbn = lastVol.NbFaceNodes( iF );
4421       if ( nbn == 3 ) {
4422         if (!hasFreeLinks ||
4423             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4424           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4425       }
4426       else if ( nbn == 4 )
4427       {
4428         if (!hasFreeLinks ||
4429             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4430           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4431       }
4432       else if ( nbn == 6 && isQuadratic )
4433       {
4434         if (!hasFreeLinks ||
4435             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4436           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4437                                                    nodes[1], nodes[3], nodes[5]));
4438       }
4439       else if ( nbn == 8 && isQuadratic )
4440       {
4441         if (!hasFreeLinks ||
4442             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4443                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4444           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4445                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4446       }
4447       else if ( nbn == 9 && isQuadratic )
4448       {
4449         if (!hasFreeLinks ||
4450             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4451                                 SMDSAbs_Face, /*noMedium=*/false) )
4452           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4453                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4454                                                    nodes[8]));
4455       }
4456       else {
4457         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4458         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4459           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4460       }
4461
4462       while ( srcElements.Length() < myLastCreatedElems.Length() )
4463         srcElements.Append( myLastCreatedElems.Last() );
4464     }
4465   } // loop on swept elements
4466 }
4467
4468 //=======================================================================
4469 //function : RotationSweep
4470 //purpose  :
4471 //=======================================================================
4472
4473 SMESH_MeshEditor::PGroupIDs
4474 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4475                                 const gp_Ax1&      theAxis,
4476                                 const double       theAngle,
4477                                 const int          theNbSteps,
4478                                 const double       theTol,
4479                                 const bool         theMakeGroups,
4480                                 const bool         theMakeWalls)
4481 {
4482   myLastCreatedElems.Clear();
4483   myLastCreatedNodes.Clear();
4484
4485   // source elements for each generated one
4486   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4487
4488   MESSAGE( "RotationSweep()");
4489   gp_Trsf aTrsf;
4490   aTrsf.SetRotation( theAxis, theAngle );
4491   gp_Trsf aTrsf2;
4492   aTrsf2.SetRotation( theAxis, theAngle/2. );
4493
4494   gp_Lin aLine( theAxis );
4495   double aSqTol = theTol * theTol;
4496
4497   SMESHDS_Mesh* aMesh = GetMeshDS();
4498
4499   TNodeOfNodeListMap mapNewNodes;
4500   TElemOfVecOfNnlmiMap mapElemNewNodes;
4501   TElemOfElemListMap newElemsMap;
4502
4503   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4504                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4505                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4506   // loop on theElems
4507   TIDSortedElemSet::iterator itElem;
4508   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4509     const SMDS_MeshElement* elem = *itElem;
4510     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4511       continue;
4512     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4513     newNodesItVec.reserve( elem->NbNodes() );
4514
4515     // loop on elem nodes
4516     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4517     while ( itN->more() )
4518     {
4519       // check if a node has been already sweeped
4520       const SMDS_MeshNode* node = cast2Node( itN->next() );
4521
4522       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4523       double coord[3];
4524       aXYZ.Coord( coord[0], coord[1], coord[2] );
4525       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4526
4527       TNodeOfNodeListMapItr nIt =
4528         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4529       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4530       if ( listNewNodes.empty() )
4531       {
4532         // check if we are to create medium nodes between corner ones
4533         bool needMediumNodes = false;
4534         if ( isQuadraticMesh )
4535         {
4536           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4537           while (it->more() && !needMediumNodes )
4538           {
4539             const SMDS_MeshElement* invElem = it->next();
4540             if ( invElem != elem && !theElems.count( invElem )) continue;
4541             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4542             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4543               needMediumNodes = true;
4544           }
4545         }
4546
4547         // make new nodes
4548         const SMDS_MeshNode * newNode = node;
4549         for ( int i = 0; i < theNbSteps; i++ ) {
4550           if ( !isOnAxis ) {
4551             if ( needMediumNodes )  // create a medium node
4552             {
4553               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4554               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4555               myLastCreatedNodes.Append(newNode);
4556               srcNodes.Append( node );
4557               listNewNodes.push_back( newNode );
4558               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4559             }
4560             else {
4561               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4562             }
4563             // create a corner node
4564             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4565             myLastCreatedNodes.Append(newNode);
4566             srcNodes.Append( node );
4567             listNewNodes.push_back( newNode );
4568           }
4569           else {
4570             listNewNodes.push_back( newNode );
4571             // if ( needMediumNodes )
4572             //   listNewNodes.push_back( newNode );
4573           }
4574         }
4575       }
4576       newNodesItVec.push_back( nIt );
4577     }
4578     // make new elements
4579     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4580   }
4581
4582   if ( theMakeWalls )
4583     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4584
4585   PGroupIDs newGroupIDs;
4586   if ( theMakeGroups )
4587     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4588
4589   return newGroupIDs;
4590 }
4591
4592
4593 //=======================================================================
4594 //function : CreateNode
4595 //purpose  :
4596 //=======================================================================
4597 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4598                                                   const double y,
4599                                                   const double z,
4600                                                   const double tolnode,
4601                                                   SMESH_SequenceOfNode& aNodes)
4602 {
4603   // myLastCreatedElems.Clear();
4604   // myLastCreatedNodes.Clear();
4605
4606   gp_Pnt P1(x,y,z);
4607   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4608
4609   // try to search in sequence of existing nodes
4610   // if aNodes.Length()>0 we 'nave to use given sequence
4611   // else - use all nodes of mesh
4612   if(aNodes.Length()>0) {
4613     int i;
4614     for(i=1; i<=aNodes.Length(); i++) {
4615       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4616       if(P1.Distance(P2)<tolnode)
4617         return aNodes.Value(i);
4618     }
4619   }
4620   else {
4621     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4622     while(itn->more()) {
4623       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4624       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4625       if(P1.Distance(P2)<tolnode)
4626         return aN;
4627     }
4628   }
4629
4630   // create new node and return it
4631   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4632   //myLastCreatedNodes.Append(NewNode);
4633   return NewNode;
4634 }
4635
4636
4637 //=======================================================================
4638 //function : ExtrusionSweep
4639 //purpose  :
4640 //=======================================================================
4641
4642 SMESH_MeshEditor::PGroupIDs
4643 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4644                                   const gp_Vec&       theStep,
4645                                   const int           theNbSteps,
4646                                   TElemOfElemListMap& newElemsMap,
4647                                   const bool          theMakeGroups,
4648                                   const int           theFlags,
4649                                   const double        theTolerance)
4650 {
4651   ExtrusParam aParams;
4652   aParams.myDir = gp_Dir(theStep);
4653   aParams.myNodes.Clear();
4654   aParams.mySteps = new TColStd_HSequenceOfReal;
4655   int i;
4656   for(i=1; i<=theNbSteps; i++)
4657     aParams.mySteps->Append(theStep.Magnitude());
4658
4659   return
4660     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4661 }
4662
4663
4664 //=======================================================================
4665 //function : ExtrusionSweep
4666 //purpose  :
4667 //=======================================================================
4668
4669 SMESH_MeshEditor::PGroupIDs
4670 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4671                                   ExtrusParam&        theParams,
4672                                   TElemOfElemListMap& newElemsMap,
4673                                   const bool          theMakeGroups,
4674                                   const int           theFlags,
4675                                   const double        theTolerance)
4676 {
4677   myLastCreatedElems.Clear();
4678   myLastCreatedNodes.Clear();
4679
4680   // source elements for each generated one
4681   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4682
4683   SMESHDS_Mesh* aMesh = GetMeshDS();
4684
4685   int nbsteps = theParams.mySteps->Length();
4686
4687   TNodeOfNodeListMap mapNewNodes;
4688   //TNodeOfNodeVecMap mapNewNodes;
4689   TElemOfVecOfNnlmiMap mapElemNewNodes;
4690   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4691
4692   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4693                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4694                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4695   // loop on theElems
4696   TIDSortedElemSet::iterator itElem;
4697   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4698     // check element type
4699     const SMDS_MeshElement* elem = *itElem;
4700     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4701       continue;
4702
4703     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4704     newNodesItVec.reserve( elem->NbNodes() );
4705
4706     // loop on elem nodes
4707     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4708     while ( itN->more() )
4709     {
4710       // check if a node has been already sweeped
4711       const SMDS_MeshNode* node = cast2Node( itN->next() );
4712       TNodeOfNodeListMap::iterator nIt =
4713         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4714       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4715       if ( listNewNodes.empty() )
4716       {
4717         // make new nodes
4718
4719         // check if we are to create medium nodes between corner ones
4720         bool needMediumNodes = false;
4721         if ( isQuadraticMesh )
4722         {
4723           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4724           while (it->more() && !needMediumNodes )
4725           {
4726             const SMDS_MeshElement* invElem = it->next();
4727             if ( invElem != elem && !theElems.count( invElem )) continue;
4728             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4729             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4730               needMediumNodes = true;
4731           }
4732         }
4733
4734         double coord[] = { node->X(), node->Y(), node->Z() };
4735         for ( int i = 0; i < nbsteps; i++ )
4736         {
4737           if ( needMediumNodes ) // create a medium node
4738           {
4739             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4740             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4741             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4742             if( theFlags & EXTRUSION_FLAG_SEW ) {
4743               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4744                                                          theTolerance, theParams.myNodes);
4745               listNewNodes.push_back( newNode );
4746             }
4747             else {
4748               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4749               myLastCreatedNodes.Append(newNode);
4750               srcNodes.Append( node );
4751               listNewNodes.push_back( newNode );
4752             }
4753           }
4754           // create a corner node
4755           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4756           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4757           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4758           if( theFlags & EXTRUSION_FLAG_SEW ) {
4759             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4760                                                        theTolerance, theParams.myNodes);
4761             listNewNodes.push_back( newNode );
4762           }
4763           else {
4764             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4765             myLastCreatedNodes.Append(newNode);
4766             srcNodes.Append( node );
4767             listNewNodes.push_back( newNode );
4768           }
4769         }
4770       }
4771       newNodesItVec.push_back( nIt );
4772     }
4773     // make new elements
4774     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4775   }
4776
4777   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4778     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4779   }
4780   PGroupIDs newGroupIDs;
4781   if ( theMakeGroups )
4782     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4783
4784   return newGroupIDs;
4785 }
4786
4787 //=======================================================================
4788 //function : ExtrusionAlongTrack
4789 //purpose  :
4790 //=======================================================================
4791 SMESH_MeshEditor::Extrusion_Error
4792 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4793                                        SMESH_subMesh*       theTrack,
4794                                        const SMDS_MeshNode* theN1,
4795                                        const bool           theHasAngles,
4796                                        list<double>&        theAngles,
4797                                        const bool           theLinearVariation,
4798                                        const bool           theHasRefPoint,
4799                                        const gp_Pnt&        theRefPoint,
4800                                        const bool           theMakeGroups)
4801 {
4802   MESSAGE("ExtrusionAlongTrack");
4803   myLastCreatedElems.Clear();
4804   myLastCreatedNodes.Clear();
4805
4806   int aNbE;
4807   std::list<double> aPrms;
4808   TIDSortedElemSet::iterator itElem;
4809
4810   gp_XYZ aGC;
4811   TopoDS_Edge aTrackEdge;
4812   TopoDS_Vertex aV1, aV2;
4813
4814   SMDS_ElemIteratorPtr aItE;
4815   SMDS_NodeIteratorPtr aItN;
4816   SMDSAbs_ElementType aTypeE;
4817
4818   TNodeOfNodeListMap mapNewNodes;
4819
4820   // 1. Check data
4821   aNbE = theElements.size();
4822   // nothing to do
4823   if ( !aNbE )
4824     return EXTR_NO_ELEMENTS;
4825
4826   // 1.1 Track Pattern
4827   ASSERT( theTrack );
4828
4829   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4830
4831   aItE = pSubMeshDS->GetElements();
4832   while ( aItE->more() ) {
4833     const SMDS_MeshElement* pE = aItE->next();
4834     aTypeE = pE->GetType();
4835     // Pattern must contain links only
4836     if ( aTypeE != SMDSAbs_Edge )
4837       return EXTR_PATH_NOT_EDGE;
4838   }
4839
4840   list<SMESH_MeshEditor_PathPoint> fullList;
4841
4842   const TopoDS_Shape& aS = theTrack->GetSubShape();
4843   // Sub-shape for the Pattern must be an Edge or Wire
4844   if( aS.ShapeType() == TopAbs_EDGE ) {
4845     aTrackEdge = TopoDS::Edge( aS );
4846     // the Edge must not be degenerated
4847     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4848       return EXTR_BAD_PATH_SHAPE;
4849     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4850     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4851     const SMDS_MeshNode* aN1 = aItN->next();
4852     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4853     const SMDS_MeshNode* aN2 = aItN->next();
4854     // starting node must be aN1 or aN2
4855     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4856       return EXTR_BAD_STARTING_NODE;
4857     aItN = pSubMeshDS->GetNodes();
4858     while ( aItN->more() ) {
4859       const SMDS_MeshNode* pNode = aItN->next();
4860       const SMDS_EdgePosition* pEPos =
4861         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4862       double aT = pEPos->GetUParameter();
4863       aPrms.push_back( aT );
4864     }
4865     //Extrusion_Error err =
4866     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4867   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4868     list< SMESH_subMesh* > LSM;
4869     TopTools_SequenceOfShape Edges;
4870     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4871     while(itSM->more()) {
4872       SMESH_subMesh* SM = itSM->next();
4873       LSM.push_back(SM);
4874       const TopoDS_Shape& aS = SM->GetSubShape();
4875       Edges.Append(aS);
4876     }
4877     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4878     int startNid = theN1->GetID();
4879     TColStd_MapOfInteger UsedNums;
4880
4881     int NbEdges = Edges.Length();
4882     int i = 1;
4883     for(; i<=NbEdges; i++) {
4884       int k = 0;
4885       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4886       for(; itLSM!=LSM.end(); itLSM++) {
4887         k++;
4888         if(UsedNums.Contains(k)) continue;
4889         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4890         SMESH_subMesh* locTrack = *itLSM;
4891         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4892         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4893         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4894         const SMDS_MeshNode* aN1 = aItN->next();
4895         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4896         const SMDS_MeshNode* aN2 = aItN->next();
4897         // starting node must be aN1 or aN2
4898         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4899         // 2. Collect parameters on the track edge
4900         aPrms.clear();
4901         aItN = locMeshDS->GetNodes();
4902         while ( aItN->more() ) {
4903           const SMDS_MeshNode* pNode = aItN->next();
4904           const SMDS_EdgePosition* pEPos =
4905             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4906           double aT = pEPos->GetUParameter();
4907           aPrms.push_back( aT );
4908         }
4909         list<SMESH_MeshEditor_PathPoint> LPP;
4910         //Extrusion_Error err =
4911         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4912         LLPPs.push_back(LPP);
4913         UsedNums.Add(k);
4914         // update startN for search following egde
4915         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4916         else startNid = aN1->GetID();
4917         break;
4918       }
4919     }
4920     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4921     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4922     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4923     for(; itPP!=firstList.end(); itPP++) {
4924       fullList.push_back( *itPP );
4925     }
4926     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4927     fullList.pop_back();
4928     itLLPP++;
4929     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4930       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4931       itPP = currList.begin();
4932       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4933       gp_Dir D1 = PP1.Tangent();
4934       gp_Dir D2 = PP2.Tangent();
4935       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4936                            (D1.Z()+D2.Z())/2 ) );
4937       PP1.SetTangent(Dnew);
4938       fullList.push_back(PP1);
4939       itPP++;
4940       for(; itPP!=firstList.end(); itPP++) {
4941         fullList.push_back( *itPP );
4942       }
4943       PP1 = fullList.back();
4944       fullList.pop_back();
4945     }
4946     // if wire not closed
4947     fullList.push_back(PP1);
4948     // else ???
4949   }
4950   else {
4951     return EXTR_BAD_PATH_SHAPE;
4952   }
4953
4954   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4955                           theHasRefPoint, theRefPoint, theMakeGroups);
4956 }
4957
4958
4959 //=======================================================================
4960 //function : ExtrusionAlongTrack
4961 //purpose  :
4962 //=======================================================================
4963 SMESH_MeshEditor::Extrusion_Error
4964 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4965                                        SMESH_Mesh*          theTrack,
4966                                        const SMDS_MeshNode* theN1,
4967                                        const bool           theHasAngles,
4968                                        list<double>&        theAngles,
4969                                        const bool           theLinearVariation,
4970                                        const bool           theHasRefPoint,
4971                                        const gp_Pnt&        theRefPoint,
4972                                        const bool           theMakeGroups)
4973 {
4974   myLastCreatedElems.Clear();
4975   myLastCreatedNodes.Clear();
4976
4977   int aNbE;
4978   std::list<double> aPrms;
4979   TIDSortedElemSet::iterator itElem;
4980
4981   gp_XYZ aGC;
4982   TopoDS_Edge aTrackEdge;
4983   TopoDS_Vertex aV1, aV2;
4984
4985   SMDS_ElemIteratorPtr aItE;
4986   SMDS_NodeIteratorPtr aItN;
4987   SMDSAbs_ElementType aTypeE;
4988
4989   TNodeOfNodeListMap mapNewNodes;
4990
4991   // 1. Check data
4992   aNbE = theElements.size();
4993   // nothing to do
4994   if ( !aNbE )
4995     return EXTR_NO_ELEMENTS;
4996
4997   // 1.1 Track Pattern
4998   ASSERT( theTrack );
4999
5000   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5001
5002   aItE = pMeshDS->elementsIterator();
5003   while ( aItE->more() ) {
5004     const SMDS_MeshElement* pE = aItE->next();
5005     aTypeE = pE->GetType();
5006     // Pattern must contain links only
5007     if ( aTypeE != SMDSAbs_Edge )
5008       return EXTR_PATH_NOT_EDGE;
5009   }
5010
5011   list<SMESH_MeshEditor_PathPoint> fullList;
5012
5013   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5014
5015   if( aS == SMESH_Mesh::PseudoShape() ) {
5016     //Mesh without shape
5017     const SMDS_MeshNode* currentNode = NULL;
5018     const SMDS_MeshNode* prevNode = theN1;
5019     std::vector<const SMDS_MeshNode*> aNodesList;
5020     aNodesList.push_back(theN1);
5021     int nbEdges = 0, conn=0;
5022     const SMDS_MeshElement* prevElem = NULL;
5023     const SMDS_MeshElement* currentElem = NULL;
5024     int totalNbEdges = theTrack->NbEdges();
5025     SMDS_ElemIteratorPtr nIt;
5026
5027     //check start node
5028     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5029       return EXTR_BAD_STARTING_NODE;
5030     }
5031
5032     conn = nbEdgeConnectivity(theN1);
5033     if(conn > 2)
5034       return EXTR_PATH_NOT_EDGE;
5035
5036     aItE = theN1->GetInverseElementIterator();
5037     prevElem = aItE->next();
5038     currentElem = prevElem;
5039     //Get all nodes
5040     if(totalNbEdges == 1 ) {
5041       nIt = currentElem->nodesIterator();
5042       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5043       if(currentNode == prevNode)
5044         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5045       aNodesList.push_back(currentNode);
5046     } else {
5047       nIt = currentElem->nodesIterator();
5048       while( nIt->more() ) {
5049         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5050         if(currentNode == prevNode)
5051           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5052         aNodesList.push_back(currentNode);
5053
5054         //case of the closed mesh
5055         if(currentNode == theN1) {
5056           nbEdges++;
5057           break;
5058         }
5059
5060         conn = nbEdgeConnectivity(currentNode);
5061         if(conn > 2) {
5062           return EXTR_PATH_NOT_EDGE;
5063         }else if( conn == 1 && nbEdges > 0 ) {
5064           //End of the path
5065           nbEdges++;
5066           break;
5067         }else {
5068           prevNode = currentNode;
5069           aItE = currentNode->GetInverseElementIterator();
5070           currentElem = aItE->next();
5071           if( currentElem  == prevElem)
5072             currentElem = aItE->next();
5073           nIt = currentElem->nodesIterator();
5074           prevElem = currentElem;
5075           nbEdges++;
5076         }
5077       }
5078     }
5079
5080     if(nbEdges != totalNbEdges)
5081       return EXTR_PATH_NOT_EDGE;
5082
5083     TopTools_SequenceOfShape Edges;
5084     double x1,x2,y1,y2,z1,z2;
5085     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5086     int startNid = theN1->GetID();
5087     for(int i = 1; i < aNodesList.size(); i++) {
5088       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5089       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5090       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5091       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5092       list<SMESH_MeshEditor_PathPoint> LPP;
5093       aPrms.clear();
5094       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5095       LLPPs.push_back(LPP);
5096       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5097       else startNid = aNodesList[i-1]->GetID();
5098
5099     }
5100
5101     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5102     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5103     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5104     for(; itPP!=firstList.end(); itPP++) {
5105       fullList.push_back( *itPP );
5106     }
5107
5108     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5109     SMESH_MeshEditor_PathPoint PP2;
5110     fullList.pop_back();
5111     itLLPP++;
5112     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5113       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5114       itPP = currList.begin();
5115       PP2 = currList.front();
5116       gp_Dir D1 = PP1.Tangent();
5117       gp_Dir D2 = PP2.Tangent();
5118       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5119                            (D1.Z()+D2.Z())/2 ) );
5120       PP1.SetTangent(Dnew);
5121       fullList.push_back(PP1);
5122       itPP++;
5123       for(; itPP!=currList.end(); itPP++) {
5124         fullList.push_back( *itPP );
5125       }
5126       PP1 = fullList.back();
5127       fullList.pop_back();
5128     }
5129     fullList.push_back(PP1);
5130
5131   } // Sub-shape for the Pattern must be an Edge or Wire
5132   else if( aS.ShapeType() == TopAbs_EDGE ) {
5133     aTrackEdge = TopoDS::Edge( aS );
5134     // the Edge must not be degenerated
5135     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5136       return EXTR_BAD_PATH_SHAPE;
5137     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5138     aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5139     const SMDS_MeshNode* aN1 = aItN->next();
5140     aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5141     const SMDS_MeshNode* aN2 = aItN->next();
5142     // starting node must be aN1 or aN2
5143     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5144       return EXTR_BAD_STARTING_NODE;
5145     aItN = pMeshDS->nodesIterator();
5146     while ( aItN->more() ) {
5147       const SMDS_MeshNode* pNode = aItN->next();
5148       if( pNode==aN1 || pNode==aN2 ) continue;
5149       const SMDS_EdgePosition* pEPos =
5150         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5151       double aT = pEPos->GetUParameter();
5152       aPrms.push_back( aT );
5153     }
5154     //Extrusion_Error err =
5155     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5156   }
5157   else if( aS.ShapeType() == TopAbs_WIRE ) {
5158     list< SMESH_subMesh* > LSM;
5159     TopTools_SequenceOfShape Edges;
5160     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5161     for(; eExp.More(); eExp.Next()) {
5162       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5163       if( BRep_Tool::Degenerated(E) ) continue;
5164       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5165       if(SM) {
5166         LSM.push_back(SM);
5167         Edges.Append(E);
5168       }
5169     }
5170     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5171     int startNid = theN1->GetID();
5172     TColStd_MapOfInteger UsedNums;
5173     int NbEdges = Edges.Length();
5174     int i = 1;
5175     for(; i<=NbEdges; i++) {
5176       int k = 0;
5177       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5178       for(; itLSM!=LSM.end(); itLSM++) {
5179         k++;
5180         if(UsedNums.Contains(k)) continue;
5181         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5182         SMESH_subMesh* locTrack = *itLSM;
5183         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5184         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5185         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5186         const SMDS_MeshNode* aN1 = aItN->next();
5187         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5188         const SMDS_MeshNode* aN2 = aItN->next();
5189         // starting node must be aN1 or aN2
5190         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5191         // 2. Collect parameters on the track edge
5192         aPrms.clear();
5193         aItN = locMeshDS->GetNodes();
5194         while ( aItN->more() ) {
5195           const SMDS_MeshNode* pNode = aItN->next();
5196           const SMDS_EdgePosition* pEPos =
5197             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5198           double aT = pEPos->GetUParameter();
5199           aPrms.push_back( aT );
5200         }
5201         list<SMESH_MeshEditor_PathPoint> LPP;
5202         //Extrusion_Error err =
5203         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5204         LLPPs.push_back(LPP);
5205         UsedNums.Add(k);
5206         // update startN for search following egde
5207         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5208         else startNid = aN1->GetID();
5209         break;
5210       }
5211     }
5212     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5213     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5214     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5215     for(; itPP!=firstList.end(); itPP++) {
5216       fullList.push_back( *itPP );
5217     }
5218     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5219     fullList.pop_back();
5220     itLLPP++;
5221     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5222       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5223       itPP = currList.begin();
5224       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5225       gp_Dir D1 = PP1.Tangent();
5226       gp_Dir D2 = PP2.Tangent();
5227       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5228                            (D1.Z()+D2.Z())/2 ) );
5229       PP1.SetTangent(Dnew);
5230       fullList.push_back(PP1);
5231       itPP++;
5232       for(; itPP!=currList.end(); itPP++) {
5233         fullList.push_back( *itPP );
5234       }
5235       PP1 = fullList.back();
5236       fullList.pop_back();
5237     }
5238     // if wire not closed
5239     fullList.push_back(PP1);
5240     // else ???
5241   }
5242   else {
5243     return EXTR_BAD_PATH_SHAPE;
5244   }
5245
5246   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5247                           theHasRefPoint, theRefPoint, theMakeGroups);
5248 }
5249
5250
5251 //=======================================================================
5252 //function : MakeEdgePathPoints
5253 //purpose  : auxilary for ExtrusionAlongTrack
5254 //=======================================================================
5255 SMESH_MeshEditor::Extrusion_Error
5256 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5257                                      const TopoDS_Edge& aTrackEdge,
5258                                      bool FirstIsStart,
5259                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5260 {
5261   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5262   aTolVec=1.e-7;
5263   aTolVec2=aTolVec*aTolVec;
5264   double aT1, aT2;
5265   TopoDS_Vertex aV1, aV2;
5266   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5267   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5268   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5269   // 2. Collect parameters on the track edge
5270   aPrms.push_front( aT1 );
5271   aPrms.push_back( aT2 );
5272   // sort parameters
5273   aPrms.sort();
5274   if( FirstIsStart ) {
5275     if ( aT1 > aT2 ) {
5276       aPrms.reverse();
5277     }
5278   }
5279   else {
5280     if ( aT2 > aT1 ) {
5281       aPrms.reverse();
5282     }
5283   }
5284   // 3. Path Points
5285   SMESH_MeshEditor_PathPoint aPP;
5286   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5287   std::list<double>::iterator aItD = aPrms.begin();
5288   for(; aItD != aPrms.end(); ++aItD) {
5289     double aT = *aItD;
5290     gp_Pnt aP3D;
5291     gp_Vec aVec;
5292     aC3D->D1( aT, aP3D, aVec );
5293     aL2 = aVec.SquareMagnitude();
5294     if ( aL2 < aTolVec2 )
5295       return EXTR_CANT_GET_TANGENT;
5296     gp_Dir aTgt( aVec );
5297     aPP.SetPnt( aP3D );
5298     aPP.SetTangent( aTgt );
5299     aPP.SetParameter( aT );
5300     LPP.push_back(aPP);
5301   }
5302   return EXTR_OK;
5303 }
5304
5305
5306 //=======================================================================
5307 //function : MakeExtrElements
5308 //purpose  : auxilary for ExtrusionAlongTrack
5309 //=======================================================================
5310 SMESH_MeshEditor::Extrusion_Error
5311 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5312                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5313                                    const bool theHasAngles,
5314                                    list<double>& theAngles,
5315                                    const bool theLinearVariation,
5316                                    const bool theHasRefPoint,
5317                                    const gp_Pnt& theRefPoint,
5318                                    const bool theMakeGroups)
5319 {
5320   MESSAGE("MakeExtrElements");
5321   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5322   int aNbTP = fullList.size();
5323   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5324   // Angles
5325   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5326     LinearAngleVariation(aNbTP-1, theAngles);
5327   }
5328   vector<double> aAngles( aNbTP );
5329   int j = 0;
5330   for(; j<aNbTP; ++j) {
5331     aAngles[j] = 0.;
5332   }
5333   if ( theHasAngles ) {
5334     double anAngle;;
5335     std::list<double>::iterator aItD = theAngles.begin();
5336     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5337       anAngle = *aItD;
5338       aAngles[j] = anAngle;
5339     }
5340   }
5341   // fill vector of path points with angles
5342   //aPPs.resize(fullList.size());
5343   j = -1;
5344   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5345   for(; itPP!=fullList.end(); itPP++) {
5346     j++;
5347     SMESH_MeshEditor_PathPoint PP = *itPP;
5348     PP.SetAngle(aAngles[j]);
5349     aPPs[j] = PP;
5350   }
5351
5352   TNodeOfNodeListMap mapNewNodes;
5353   TElemOfVecOfNnlmiMap mapElemNewNodes;
5354   TElemOfElemListMap newElemsMap;
5355   TIDSortedElemSet::iterator itElem;
5356   double aX, aY, aZ;
5357   int aNb;
5358   SMDSAbs_ElementType aTypeE;
5359   // source elements for each generated one
5360   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5361
5362   // 3. Center of rotation aV0
5363   gp_Pnt aV0 = theRefPoint;
5364   gp_XYZ aGC;
5365   if ( !theHasRefPoint ) {
5366     aNb = 0;
5367     aGC.SetCoord( 0.,0.,0. );
5368
5369     itElem = theElements.begin();
5370     for ( ; itElem != theElements.end(); itElem++ ) {
5371       const SMDS_MeshElement* elem = *itElem;
5372
5373       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5374       while ( itN->more() ) {
5375         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5376         aX = node->X();
5377         aY = node->Y();
5378         aZ = node->Z();
5379
5380         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5381           list<const SMDS_MeshNode*> aLNx;
5382           mapNewNodes[node] = aLNx;
5383           //
5384           gp_XYZ aXYZ( aX, aY, aZ );
5385           aGC += aXYZ;
5386           ++aNb;
5387         }
5388       }
5389     }
5390     aGC /= aNb;
5391     aV0.SetXYZ( aGC );
5392   } // if (!theHasRefPoint) {
5393   mapNewNodes.clear();
5394
5395   // 4. Processing the elements
5396   SMESHDS_Mesh* aMesh = GetMeshDS();
5397
5398   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5399     // check element type
5400     const SMDS_MeshElement* elem = *itElem;
5401     aTypeE = elem->GetType();
5402     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5403       continue;
5404
5405     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5406     newNodesItVec.reserve( elem->NbNodes() );
5407
5408     // loop on elem nodes
5409     int nodeIndex = -1;
5410     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5411     while ( itN->more() )
5412     {
5413       ++nodeIndex;
5414       // check if a node has been already processed
5415       const SMDS_MeshNode* node =
5416         static_cast<const SMDS_MeshNode*>( itN->next() );
5417       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5418       if ( nIt == mapNewNodes.end() ) {
5419         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5420         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5421
5422         // make new nodes
5423         aX = node->X();  aY = node->Y(); aZ = node->Z();
5424
5425         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5426         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5427         gp_Ax1 anAx1, anAxT1T0;
5428         gp_Dir aDT1x, aDT0x, aDT1T0;
5429
5430         aTolAng=1.e-4;
5431
5432         aV0x = aV0;
5433         aPN0.SetCoord(aX, aY, aZ);
5434
5435         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5436         aP0x = aPP0.Pnt();
5437         aDT0x= aPP0.Tangent();
5438         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5439
5440         for ( j = 1; j < aNbTP; ++j ) {
5441           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5442           aP1x = aPP1.Pnt();
5443           aDT1x = aPP1.Tangent();
5444           aAngle1x = aPP1.Angle();
5445
5446           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5447           // Translation
5448           gp_Vec aV01x( aP0x, aP1x );
5449           aTrsf.SetTranslation( aV01x );
5450
5451           // traslated point
5452           aV1x = aV0x.Transformed( aTrsf );
5453           aPN1 = aPN0.Transformed( aTrsf );
5454
5455           // rotation 1 [ T1,T0 ]
5456           aAngleT1T0=-aDT1x.Angle( aDT0x );
5457           if (fabs(aAngleT1T0) > aTolAng) {
5458             aDT1T0=aDT1x^aDT0x;
5459             anAxT1T0.SetLocation( aV1x );
5460             anAxT1T0.SetDirection( aDT1T0 );
5461             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5462
5463             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5464           }
5465
5466           // rotation 2
5467           if ( theHasAngles ) {
5468             anAx1.SetLocation( aV1x );
5469             anAx1.SetDirection( aDT1x );
5470             aTrsfRot.SetRotation( anAx1, aAngle1x );
5471
5472             aPN1 = aPN1.Transformed( aTrsfRot );
5473           }
5474
5475           // make new node
5476           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5477           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5478             // create additional node
5479             double x = ( aPN1.X() + aPN0.X() )/2.;
5480             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5481             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5482             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5483             myLastCreatedNodes.Append(newNode);
5484             srcNodes.Append( node );
5485             listNewNodes.push_back( newNode );
5486           }
5487           aX = aPN1.X();
5488           aY = aPN1.Y();
5489           aZ = aPN1.Z();
5490           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5491           myLastCreatedNodes.Append(newNode);
5492           srcNodes.Append( node );
5493           listNewNodes.push_back( newNode );
5494
5495           aPN0 = aPN1;
5496           aP0x = aP1x;
5497           aV0x = aV1x;
5498           aDT0x = aDT1x;
5499         }
5500       }
5501
5502       else {
5503         // if current elem is quadratic and current node is not medium
5504         // we have to check - may be it is needed to insert additional nodes
5505         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5506           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5507           if(listNewNodes.size()==aNbTP-1) {
5508             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5509             gp_XYZ P(node->X(), node->Y(), node->Z());
5510             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5511             int i;
5512             for(i=0; i<aNbTP-1; i++) {
5513               const SMDS_MeshNode* N = *it;
5514               double x = ( N->X() + P.X() )/2.;
5515               double y = ( N->Y() + P.Y() )/2.;
5516               double z = ( N->Z() + P.Z() )/2.;
5517               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5518               srcNodes.Append( node );
5519               myLastCreatedNodes.Append(newN);
5520               aNodes[2*i] = newN;
5521               aNodes[2*i+1] = N;
5522               P = gp_XYZ(N->X(),N->Y(),N->Z());
5523             }
5524             listNewNodes.clear();
5525             for(i=0; i<2*(aNbTP-1); i++) {
5526               listNewNodes.push_back(aNodes[i]);
5527             }
5528           }
5529         }
5530       }
5531
5532       newNodesItVec.push_back( nIt );
5533     }
5534     // make new elements
5535     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5536     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5537     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5538   }
5539
5540   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5541
5542   if ( theMakeGroups )
5543     generateGroups( srcNodes, srcElems, "extruded");
5544
5545   return EXTR_OK;
5546 }
5547
5548
5549 //=======================================================================
5550 //function : LinearAngleVariation
5551 //purpose  : auxilary for ExtrusionAlongTrack
5552 //=======================================================================
5553 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5554                                             list<double>& Angles)
5555 {
5556   int nbAngles = Angles.size();
5557   if( nbSteps > nbAngles ) {
5558     vector<double> theAngles(nbAngles);
5559     list<double>::iterator it = Angles.begin();
5560     int i = -1;
5561     for(; it!=Angles.end(); it++) {
5562       i++;
5563       theAngles[i] = (*it);
5564     }
5565     list<double> res;
5566     double rAn2St = double( nbAngles ) / double( nbSteps );
5567     double angPrev = 0, angle;
5568     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5569       double angCur = rAn2St * ( iSt+1 );
5570       double angCurFloor  = floor( angCur );
5571       double angPrevFloor = floor( angPrev );
5572       if ( angPrevFloor == angCurFloor )
5573         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5574       else {
5575         int iP = int( angPrevFloor );
5576         double angPrevCeil = ceil(angPrev);
5577         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5578
5579         int iC = int( angCurFloor );
5580         if ( iC < nbAngles )
5581           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5582
5583         iP = int( angPrevCeil );
5584         while ( iC-- > iP )
5585           angle += theAngles[ iC ];
5586       }
5587       res.push_back(angle);
5588       angPrev = angCur;
5589     }
5590     Angles.clear();
5591     it = res.begin();
5592     for(; it!=res.end(); it++)
5593       Angles.push_back( *it );
5594   }
5595 }
5596
5597
5598 //================================================================================
5599 /*!
5600  * \brief Move or copy theElements applying theTrsf to their nodes
5601  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5602  *  \param theTrsf - transformation to apply
5603  *  \param theCopy - if true, create translated copies of theElems
5604  *  \param theMakeGroups - if true and theCopy, create translated groups
5605  *  \param theTargetMesh - mesh to copy translated elements into
5606  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5607  */
5608 //================================================================================
5609
5610 SMESH_MeshEditor::PGroupIDs
5611 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5612                              const gp_Trsf&     theTrsf,
5613                              const bool         theCopy,
5614                              const bool         theMakeGroups,
5615                              SMESH_Mesh*        theTargetMesh)
5616 {
5617   myLastCreatedElems.Clear();
5618   myLastCreatedNodes.Clear();
5619
5620   bool needReverse = false;
5621   string groupPostfix;
5622   switch ( theTrsf.Form() ) {
5623   case gp_PntMirror:
5624     MESSAGE("gp_PntMirror");
5625     needReverse = true;
5626     groupPostfix = "mirrored";
5627     break;
5628   case gp_Ax1Mirror:
5629     MESSAGE("gp_Ax1Mirror");
5630     groupPostfix = "mirrored";
5631     break;
5632   case gp_Ax2Mirror:
5633     MESSAGE("gp_Ax2Mirror");
5634     needReverse = true;
5635     groupPostfix = "mirrored";
5636     break;
5637   case gp_Rotation:
5638     MESSAGE("gp_Rotation");
5639     groupPostfix = "rotated";
5640     break;
5641   case gp_Translation:
5642     MESSAGE("gp_Translation");
5643     groupPostfix = "translated";
5644     break;
5645   case gp_Scale:
5646     MESSAGE("gp_Scale");
5647     groupPostfix = "scaled";
5648     break;
5649   case gp_CompoundTrsf: // different scale by axis
5650     MESSAGE("gp_CompoundTrsf");
5651     groupPostfix = "scaled";
5652     break;
5653   default:
5654     MESSAGE("default");
5655     needReverse = false;
5656     groupPostfix = "transformed";
5657   }
5658
5659   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5660   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5661   SMESHDS_Mesh* aMesh    = GetMeshDS();
5662
5663
5664   // map old node to new one
5665   TNodeNodeMap nodeMap;
5666
5667   // elements sharing moved nodes; those of them which have all
5668   // nodes mirrored but are not in theElems are to be reversed
5669   TIDSortedElemSet inverseElemSet;
5670
5671   // source elements for each generated one
5672   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5673
5674   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5675   TIDSortedElemSet orphanNode;
5676
5677   if ( theElems.empty() ) // transform the whole mesh
5678   {
5679     // add all elements
5680     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5681     while ( eIt->more() ) theElems.insert( eIt->next() );
5682     // add orphan nodes
5683     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5684     while ( nIt->more() )
5685     {
5686       const SMDS_MeshNode* node = nIt->next();
5687       if ( node->NbInverseElements() == 0)
5688         orphanNode.insert( node );
5689     }
5690   }
5691
5692   // loop on elements to transform nodes : first orphan nodes then elems
5693   TIDSortedElemSet::iterator itElem;
5694   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5695   for (int i=0; i<2; i++)
5696   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5697     const SMDS_MeshElement* elem = *itElem;
5698     if ( !elem )
5699       continue;
5700
5701     // loop on elem nodes
5702     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5703     while ( itN->more() ) {
5704
5705       const SMDS_MeshNode* node = cast2Node( itN->next() );
5706       // check if a node has been already transformed
5707       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5708         nodeMap.insert( make_pair ( node, node ));
5709       if ( !n2n_isnew.second )
5710         continue;
5711
5712       double coord[3];
5713       coord[0] = node->X();
5714       coord[1] = node->Y();
5715       coord[2] = node->Z();
5716       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5717       if ( theTargetMesh ) {
5718         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5719         n2n_isnew.first->second = newNode;
5720         myLastCreatedNodes.Append(newNode);
5721         srcNodes.Append( node );
5722       }
5723       else if ( theCopy ) {
5724         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5725         n2n_isnew.first->second = newNode;
5726         myLastCreatedNodes.Append(newNode);
5727         srcNodes.Append( node );
5728       }
5729       else {
5730         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5731         // node position on shape becomes invalid
5732         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5733           ( SMDS_SpacePosition::originSpacePosition() );
5734       }
5735
5736       // keep inverse elements
5737       if ( !theCopy && !theTargetMesh && needReverse ) {
5738         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5739         while ( invElemIt->more() ) {
5740           const SMDS_MeshElement* iel = invElemIt->next();
5741           inverseElemSet.insert( iel );
5742         }
5743       }
5744     }
5745   }
5746
5747   // either create new elements or reverse mirrored ones
5748   if ( !theCopy && !needReverse && !theTargetMesh )
5749     return PGroupIDs();
5750
5751   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5752   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5753     theElems.insert( *invElemIt );
5754
5755   // Replicate or reverse elements
5756
5757   std::vector<int> iForw;
5758   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5759   {
5760     const SMDS_MeshElement* elem = *itElem;
5761     if ( !elem ) continue;
5762
5763     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5764     int                  nbNodes  = elem->NbNodes();
5765     if ( geomType == SMDSGeom_NONE ) continue; // node
5766
5767     switch ( geomType ) {
5768
5769     case SMDSGeom_POLYGON:  // ---------------------- polygon
5770       {
5771         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5772         int iNode = 0;
5773         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5774         while (itN->more()) {
5775           const SMDS_MeshNode* node =
5776             static_cast<const SMDS_MeshNode*>(itN->next());
5777           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5778           if (nodeMapIt == nodeMap.end())
5779             break; // not all nodes transformed
5780           if (needReverse) {
5781             // reverse mirrored faces and volumes
5782             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5783           } else {
5784             poly_nodes[iNode] = (*nodeMapIt).second;
5785           }
5786           iNode++;
5787         }
5788         if ( iNode != nbNodes )
5789           continue; // not all nodes transformed
5790
5791         if ( theTargetMesh ) {
5792           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5793           srcElems.Append( elem );
5794         }
5795         else if ( theCopy ) {
5796           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5797           srcElems.Append( elem );
5798         }
5799         else {
5800           aMesh->ChangePolygonNodes(elem, poly_nodes);
5801         }
5802       }
5803       break;
5804
5805     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5806       {
5807         const SMDS_VtkVolume* aPolyedre =
5808           dynamic_cast<const SMDS_VtkVolume*>( elem );
5809         if (!aPolyedre) {
5810           MESSAGE("Warning: bad volumic element");
5811           continue;
5812         }
5813
5814         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5815         vector<int> quantities; quantities.reserve( nbNodes );
5816
5817         bool allTransformed = true;
5818         int nbFaces = aPolyedre->NbFaces();
5819         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5820           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5821           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5822             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5823             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5824             if (nodeMapIt == nodeMap.end()) {
5825               allTransformed = false; // not all nodes transformed
5826             } else {
5827               poly_nodes.push_back((*nodeMapIt).second);
5828             }
5829             if ( needReverse && allTransformed )
5830               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5831           }
5832           quantities.push_back(nbFaceNodes);
5833         }
5834         if ( !allTransformed )
5835           continue; // not all nodes transformed
5836
5837         if ( theTargetMesh ) {
5838           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5839           srcElems.Append( elem );
5840         }
5841         else if ( theCopy ) {
5842           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5843           srcElems.Append( elem );
5844         }
5845         else {
5846           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5847         }
5848       }
5849       break;
5850
5851     case SMDSGeom_BALL: // -------------------- Ball
5852       {
5853         if ( !theCopy && !theTargetMesh ) continue;
5854
5855         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5856         if (nodeMapIt == nodeMap.end())
5857           continue; // not all nodes transformed
5858
5859         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5860         if ( theTargetMesh ) {
5861           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5862           srcElems.Append( elem );
5863         }
5864         else {
5865           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5866           srcElems.Append( elem );
5867         }
5868       }
5869       break;
5870
5871     default: // ----------------------- Regular elements
5872
5873       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5874       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5875       const std::vector<int>& i = needReverse ? iRev : iForw;
5876
5877       // find transformed nodes
5878       vector<const SMDS_MeshNode*> nodes(nbNodes);
5879       int iNode = 0;
5880       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5881       while ( itN->more() ) {
5882         const SMDS_MeshNode* node =
5883           static_cast<const SMDS_MeshNode*>( itN->next() );
5884         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5885         if ( nodeMapIt == nodeMap.end() )
5886           break; // not all nodes transformed
5887         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5888       }
5889       if ( iNode != nbNodes )
5890         continue; // not all nodes transformed
5891
5892       if ( theTargetMesh ) {
5893         if ( SMDS_MeshElement* copy =
5894              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5895           myLastCreatedElems.Append( copy );
5896           srcElems.Append( elem );
5897         }
5898       }
5899       else if ( theCopy ) {
5900         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5901           srcElems.Append( elem );
5902       }
5903       else {
5904         // reverse element as it was reversed by transformation
5905         if ( nbNodes > 2 )
5906           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5907       }
5908     } // switch ( geomType )
5909
5910   } // loop on elements
5911
5912   PGroupIDs newGroupIDs;
5913
5914   if ( ( theMakeGroups && theCopy ) ||
5915        ( theMakeGroups && theTargetMesh ) )
5916     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5917
5918   return newGroupIDs;
5919 }
5920
5921 //=======================================================================
5922 /*!
5923  * \brief Create groups of elements made during transformation
5924  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5925  * \param elemGens - elements making corresponding myLastCreatedElems
5926  * \param postfix - to append to names of new groups
5927  */
5928 //=======================================================================
5929
5930 SMESH_MeshEditor::PGroupIDs
5931 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5932                                  const SMESH_SequenceOfElemPtr& elemGens,
5933                                  const std::string&             postfix,
5934                                  SMESH_Mesh*                    targetMesh)
5935 {
5936   PGroupIDs newGroupIDs( new list<int> );
5937   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5938
5939   // Sort existing groups by types and collect their names
5940
5941   // to store an old group and a generated new one
5942   typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5943   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5944   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5945   // group names
5946   set< string > groupNames;
5947
5948   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5949   if ( !groupIt->more() ) return newGroupIDs;
5950
5951   int newGroupID = mesh->GetGroupIds().back()+1;
5952   while ( groupIt->more() )
5953   {
5954     SMESH_Group * group = groupIt->next();
5955     if ( !group ) continue;
5956     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5957     if ( !groupDS || groupDS->IsEmpty() ) continue;
5958     groupNames.insert( group->GetName() );
5959     groupDS->SetStoreName( group->GetName() );
5960     SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5961                                                  groupDS->GetType() );
5962     groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5963     orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5964   }
5965
5966   // Loop on nodes and elements to add them in new groups
5967
5968   for ( int isNodes = 0; isNodes < 2; ++isNodes )
5969   {
5970     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
5971     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5972     if ( gens.Length() != elems.Length() )
5973       throw SALOME_Exception(LOCALIZED("invalid args"));
5974
5975     // loop on created elements
5976     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5977     {
5978       const SMDS_MeshElement* sourceElem = gens( iElem );
5979       if ( !sourceElem ) {
5980         MESSAGE("generateGroups(): NULL source element");
5981         continue;
5982       }
5983       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5984       if ( groupsOldNew.empty() ) { // no groups of this type at all
5985         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5986           ++iElem; // skip all elements made by sourceElem
5987         continue;
5988       }
5989       // collect all elements made by sourceElem
5990       list< const SMDS_MeshElement* > resultElems;
5991       if ( const SMDS_MeshElement* resElem = elems( iElem ))
5992         if ( resElem != sourceElem )
5993           resultElems.push_back( resElem );
5994       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5995         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5996           if ( resElem != sourceElem )
5997             resultElems.push_back( resElem );
5998
5999       // add resultElems to groups made by ones the sourceElem belongs to
6000       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6001       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6002       {
6003         SMESHDS_GroupBase* oldGroup = gOldNew->first;
6004         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6005         {
6006           // fill in a new group
6007           SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
6008           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6009           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6010             newGroup.Add( *resElemIt );
6011         }
6012       }
6013     } // loop on created elements
6014   }// loop on nodes and elements
6015
6016   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6017
6018   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6019   {
6020     SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
6021     SMESHDS_Group*     newGroupDS = orderedOldNewGroups[i]->second;
6022     if ( newGroupDS->IsEmpty() )
6023     {
6024       mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6025     }
6026     else
6027     {
6028       // make a name
6029       string name = oldGroupDS->GetStoreName();
6030       if ( !targetMesh ) {
6031         name += "_";
6032         name += postfix;
6033         int nb = 1;
6034         while ( !groupNames.insert( name ).second ) // name exists
6035           name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
6036       }
6037       newGroupDS->SetStoreName( name.c_str() );
6038
6039       // make a SMESH_Groups
6040       mesh->AddGroup( newGroupDS );
6041       newGroupIDs->push_back( newGroupDS->GetID() );
6042
6043       // set group type
6044       newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6045     }
6046   }
6047
6048   return newGroupIDs;
6049 }
6050
6051 //================================================================================
6052 /*!
6053  * \brief Return list of group of nodes close to each other within theTolerance
6054  *        Search among theNodes or in the whole mesh if theNodes is empty using
6055  *        an Octree algorithm
6056  */
6057 //================================================================================
6058
6059 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6060                                             const double         theTolerance,
6061                                             TListOfListOfNodes & theGroupsOfNodes)
6062 {
6063   myLastCreatedElems.Clear();
6064   myLastCreatedNodes.Clear();
6065
6066   if ( theNodes.empty() )
6067   { // get all nodes in the mesh
6068     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6069     while ( nIt->more() )
6070       theNodes.insert( theNodes.end(),nIt->next());
6071   }
6072
6073   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6074 }
6075
6076
6077 //=======================================================================
6078 /*!
6079  * \brief Implementation of search for the node closest to point
6080  */
6081 //=======================================================================
6082
6083 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6084 {
6085   //---------------------------------------------------------------------
6086   /*!
6087    * \brief Constructor
6088    */
6089   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6090   {
6091     myMesh = ( SMESHDS_Mesh* ) theMesh;
6092
6093     TIDSortedNodeSet nodes;
6094     if ( theMesh ) {
6095       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6096       while ( nIt->more() )
6097         nodes.insert( nodes.end(), nIt->next() );
6098     }
6099     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6100
6101     // get max size of a leaf box
6102     SMESH_OctreeNode* tree = myOctreeNode;
6103     while ( !tree->isLeaf() )
6104     {
6105       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6106       if ( cIt->more() )
6107         tree = cIt->next();
6108     }
6109     myHalfLeafSize = tree->maxSize() / 2.;
6110   }
6111
6112   //---------------------------------------------------------------------
6113   /*!
6114    * \brief Move node and update myOctreeNode accordingly
6115    */
6116   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6117   {
6118     myOctreeNode->UpdateByMoveNode( node, toPnt );
6119     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6120   }
6121
6122   //---------------------------------------------------------------------
6123   /*!
6124    * \brief Do it's job
6125    */
6126   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6127   {
6128     map<double, const SMDS_MeshNode*> dist2Nodes;
6129     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6130     if ( !dist2Nodes.empty() )
6131       return dist2Nodes.begin()->second;
6132     list<const SMDS_MeshNode*> nodes;
6133     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6134
6135     double minSqDist = DBL_MAX;
6136     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6137     {
6138       // sort leafs by their distance from thePnt
6139       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6140       TDistTreeMap treeMap;
6141       list< SMESH_OctreeNode* > treeList;
6142       list< SMESH_OctreeNode* >::iterator trIt;
6143       treeList.push_back( myOctreeNode );
6144
6145       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6146       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6147       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6148       {
6149         SMESH_OctreeNode* tree = *trIt;
6150         if ( !tree->isLeaf() ) // put children to the queue
6151         {
6152           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6153           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6154           while ( cIt->more() )
6155             treeList.push_back( cIt->next() );
6156         }
6157         else if ( tree->NbNodes() ) // put a tree to the treeMap
6158         {
6159           const Bnd_B3d& box = *tree->getBox();
6160           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6161           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6162           if ( !it_in.second ) // not unique distance to box center
6163             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6164         }
6165       }
6166       // find distance after which there is no sense to check tree's
6167       double sqLimit = DBL_MAX;
6168       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6169       if ( treeMap.size() > 5 ) {
6170         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6171         const Bnd_B3d& box = *closestTree->getBox();
6172         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6173         sqLimit = limit * limit;
6174       }
6175       // get all nodes from trees
6176       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6177         if ( sqDist_tree->first > sqLimit )
6178           break;
6179         SMESH_OctreeNode* tree = sqDist_tree->second;
6180         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6181       }
6182     }
6183     // find closest among nodes
6184     minSqDist = DBL_MAX;
6185     const SMDS_MeshNode* closestNode = 0;
6186     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6187     for ( ; nIt != nodes.end(); ++nIt ) {
6188       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6189       if ( minSqDist > sqDist ) {
6190         closestNode = *nIt;
6191         minSqDist = sqDist;
6192       }
6193     }
6194     return closestNode;
6195   }
6196
6197   //---------------------------------------------------------------------
6198   /*!
6199    * \brief Destructor
6200    */
6201   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6202
6203   //---------------------------------------------------------------------
6204   /*!
6205    * \brief Return the node tree
6206    */
6207   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6208
6209 private:
6210   SMESH_OctreeNode* myOctreeNode;
6211   SMESHDS_Mesh*     myMesh;
6212   double            myHalfLeafSize; // max size of a leaf box
6213 };
6214
6215 //=======================================================================
6216 /*!
6217  * \brief Return SMESH_NodeSearcher
6218  */
6219 //=======================================================================
6220
6221 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6222 {
6223   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6224 }
6225
6226 // ========================================================================
6227 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6228 {
6229   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6230   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6231   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6232
6233   //=======================================================================
6234   /*!
6235    * \brief Octal tree of bounding boxes of elements
6236    */
6237   //=======================================================================
6238
6239   class ElementBndBoxTree : public SMESH_Octree
6240   {
6241   public:
6242
6243     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6244                       SMDSAbs_ElementType  elemType,
6245                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6246                       double               tolerance = NodeRadius );
6247     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6248     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6249     void getElementsInSphere ( const gp_XYZ& center,
6250                                const double  radius, TIDSortedElemSet& foundElems);
6251     size_t getSize() { return std::max( _size, _elements.size() ); }
6252     ~ElementBndBoxTree();
6253
6254   protected:
6255     ElementBndBoxTree():_size(0) {}
6256     SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6257     void          buildChildrenData();
6258     Bnd_B3d*      buildRootBox();
6259   private:
6260     //!< Bounding box of element
6261     struct ElementBox : public Bnd_B3d
6262     {
6263       const SMDS_MeshElement* _element;
6264       int                     _refCount; // an ElementBox can be included in several tree branches
6265       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6266     };
6267     vector< ElementBox* > _elements;
6268     size_t                _size;
6269   };
6270
6271   //================================================================================
6272   /*!
6273    * \brief ElementBndBoxTree creation
6274    */
6275   //================================================================================
6276
6277   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6278     :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6279   {
6280     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6281     _elements.reserve( nbElems );
6282
6283     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6284     while ( elemIt->more() )
6285       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6286
6287     compute();
6288   }
6289
6290   //================================================================================
6291   /*!
6292    * \brief Destructor
6293    */
6294   //================================================================================
6295
6296   ElementBndBoxTree::~ElementBndBoxTree()
6297   {
6298     for ( int i = 0; i < _elements.size(); ++i )
6299       if ( --_elements[i]->_refCount <= 0 )
6300         delete _elements[i];
6301   }
6302
6303   //================================================================================
6304   /*!
6305    * \brief Return the maximal box
6306    */
6307   //================================================================================
6308
6309   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6310   {
6311     Bnd_B3d* box = new Bnd_B3d;
6312     for ( int i = 0; i < _elements.size(); ++i )
6313       box->Add( *_elements[i] );
6314     return box;
6315   }
6316
6317   //================================================================================
6318   /*!
6319    * \brief Redistrubute element boxes among children
6320    */
6321   //================================================================================
6322
6323   void ElementBndBoxTree::buildChildrenData()
6324   {
6325     for ( int i = 0; i < _elements.size(); ++i )
6326     {
6327       for (int j = 0; j < 8; j++)
6328       {
6329         if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6330         {
6331           _elements[i]->_refCount++;
6332           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6333         }
6334       }
6335       _elements[i]->_refCount--;
6336     }
6337     _size = _elements.size();
6338     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6339
6340     for (int j = 0; j < 8; j++)
6341     {
6342       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6343       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6344         child->myIsLeaf = true;
6345
6346       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6347         SMESHUtils::CompactVector( child->_elements );
6348     }
6349   }
6350
6351   //================================================================================
6352   /*!
6353    * \brief Return elements which can include the point
6354    */
6355   //================================================================================
6356
6357   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6358                                                 TIDSortedElemSet& foundElems)
6359   {
6360     if ( getBox()->IsOut( point.XYZ() ))
6361       return;
6362
6363     if ( isLeaf() )
6364     {
6365       for ( int i = 0; i < _elements.size(); ++i )
6366         if ( !_elements[i]->IsOut( point.XYZ() ))
6367           foundElems.insert( _elements[i]->_element );
6368     }
6369     else
6370     {
6371       for (int i = 0; i < 8; i++)
6372         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6373     }
6374   }
6375
6376   //================================================================================
6377   /*!
6378    * \brief Return elements which can be intersected by the line
6379    */
6380   //================================================================================
6381
6382   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6383                                                TIDSortedElemSet& foundElems)
6384   {
6385     if ( getBox()->IsOut( line ))
6386       return;
6387
6388     if ( isLeaf() )
6389     {
6390       for ( int i = 0; i < _elements.size(); ++i )
6391         if ( !_elements[i]->IsOut( line ))
6392           foundElems.insert( _elements[i]->_element );
6393     }
6394     else
6395     {
6396       for (int i = 0; i < 8; i++)
6397         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6398     }
6399   }
6400
6401   //================================================================================
6402   /*!
6403    * \brief Return elements from leaves intersecting the sphere
6404    */
6405   //================================================================================
6406
6407   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6408                                                 const double      radius,
6409                                                 TIDSortedElemSet& foundElems)
6410   {
6411     if ( getBox()->IsOut( center, radius ))
6412       return;
6413
6414     if ( isLeaf() )
6415     {
6416       for ( int i = 0; i < _elements.size(); ++i )
6417         if ( !_elements[i]->IsOut( center, radius ))
6418           foundElems.insert( _elements[i]->_element );
6419     }
6420     else
6421     {
6422       for (int i = 0; i < 8; i++)
6423         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6424     }
6425   }
6426
6427   //================================================================================
6428   /*!
6429    * \brief Construct the element box
6430    */
6431   //================================================================================
6432
6433   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6434   {
6435     _element  = elem;
6436     _refCount = 1;
6437     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6438     while ( nIt->more() )
6439       Add( SMESH_TNodeXYZ( nIt->next() ));
6440     Enlarge( tolerance );
6441   }
6442
6443 } // namespace
6444
6445 //=======================================================================
6446 /*!
6447  * \brief Implementation of search for the elements by point and
6448  *        of classification of point in 2D mesh
6449  */
6450 //=======================================================================
6451
6452 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6453 {
6454   SMESHDS_Mesh*                _mesh;
6455   SMDS_ElemIteratorPtr         _meshPartIt;
6456   ElementBndBoxTree*           _ebbTree;
6457   SMESH_NodeSearcherImpl*      _nodeSearcher;
6458   SMDSAbs_ElementType          _elementType;
6459   double                       _tolerance;
6460   bool                         _outerFacesFound;
6461   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6462
6463   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6464     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6465   ~SMESH_ElementSearcherImpl()
6466   {
6467     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6468     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6469   }
6470   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6471                                   SMDSAbs_ElementType                type,
6472                                   vector< const SMDS_MeshElement* >& foundElements);
6473   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6474   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6475                                                  SMDSAbs_ElementType type );
6476
6477   void GetElementsNearLine( const gp_Ax1&                      line,
6478                             SMDSAbs_ElementType                type,
6479                             vector< const SMDS_MeshElement* >& foundElems);
6480   double getTolerance();
6481   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6482                             const double tolerance, double & param);
6483   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6484   bool isOuterBoundary(const SMDS_MeshElement* face) const
6485   {
6486     return _outerFaces.empty() || _outerFaces.count(face);
6487   }
6488   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6489   {
6490     const SMDS_MeshElement* _face;
6491     gp_Vec                  _faceNorm;
6492     bool                    _coincides; //!< the line lays in face plane
6493     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6494       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6495   };
6496   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6497   {
6498     SMESH_TLink      _link;
6499     TIDSortedElemSet _faces;
6500     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6501       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6502   };
6503 };
6504
6505 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6506 {
6507   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6508              << ", _coincides="<<i._coincides << ")";
6509 }
6510
6511 //=======================================================================
6512 /*!
6513  * \brief define tolerance for search
6514  */
6515 //=======================================================================
6516
6517 double SMESH_ElementSearcherImpl::getTolerance()
6518 {
6519   if ( _tolerance < 0 )
6520   {
6521     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6522
6523     _tolerance = 0;
6524     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6525     {
6526       double boxSize = _nodeSearcher->getTree()->maxSize();
6527       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6528     }
6529     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6530     {
6531       double boxSize = _ebbTree->maxSize();
6532       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6533     }
6534     if ( _tolerance == 0 )
6535     {
6536       // define tolerance by size of a most complex element
6537       int complexType = SMDSAbs_Volume;
6538       while ( complexType > SMDSAbs_All &&
6539               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6540         --complexType;
6541       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6542       double elemSize;
6543       if ( complexType == int( SMDSAbs_Node ))
6544       {
6545         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6546         elemSize = 1;
6547         if ( meshInfo.NbNodes() > 2 )
6548           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6549       }
6550       else
6551       {
6552         SMDS_ElemIteratorPtr elemIt =
6553             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6554         const SMDS_MeshElement* elem = elemIt->next();
6555         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6556         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6557         elemSize = 0;
6558         while ( nodeIt->more() )
6559         {
6560           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6561           elemSize = max( dist, elemSize );
6562         }
6563       }
6564       _tolerance = 1e-4 * elemSize;
6565     }
6566   }
6567   return _tolerance;
6568 }
6569
6570 //================================================================================
6571 /*!
6572  * \brief Find intersection of the line and an edge of face and return parameter on line
6573  */
6574 //================================================================================
6575
6576 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6577                                                      const SMDS_MeshElement* face,
6578                                                      const double            tol,
6579                                                      double &                param)
6580 {
6581   int nbInts = 0;
6582   param = 0;
6583
6584   GeomAPI_ExtremaCurveCurve anExtCC;
6585   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6586
6587   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6588   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6589   {
6590     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6591                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6592     anExtCC.Init( lineCurve, edge);
6593     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6594     {
6595       Quantity_Parameter pl, pe;
6596       anExtCC.LowerDistanceParameters( pl, pe );
6597       param += pl;
6598       if ( ++nbInts == 2 )
6599         break;
6600     }
6601   }
6602   if ( nbInts > 0 ) param /= nbInts;
6603   return nbInts > 0;
6604 }
6605 //================================================================================
6606 /*!
6607  * \brief Find all faces belonging to the outer boundary of mesh
6608  */
6609 //================================================================================
6610
6611 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6612 {
6613   if ( _outerFacesFound ) return;
6614
6615   // Collect all outer faces by passing from one outer face to another via their links
6616   // and BTW find out if there are internal faces at all.
6617
6618   // checked links and links where outer boundary meets internal one
6619   set< SMESH_TLink > visitedLinks, seamLinks;
6620
6621   // links to treat with already visited faces sharing them
6622   list < TFaceLink > startLinks;
6623
6624   // load startLinks with the first outerFace
6625   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6626   _outerFaces.insert( outerFace );
6627
6628   TIDSortedElemSet emptySet;
6629   while ( !startLinks.empty() )
6630   {
6631     const SMESH_TLink& link  = startLinks.front()._link;
6632     TIDSortedElemSet&  faces = startLinks.front()._faces;
6633
6634     outerFace = *faces.begin();
6635     // find other faces sharing the link
6636     const SMDS_MeshElement* f;
6637     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6638       faces.insert( f );
6639
6640     // select another outer face among the found
6641     const SMDS_MeshElement* outerFace2 = 0;
6642     if ( faces.size() == 2 )
6643     {
6644       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6645     }
6646     else if ( faces.size() > 2 )
6647     {
6648       seamLinks.insert( link );
6649
6650       // link direction within the outerFace
6651       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6652                    SMESH_TNodeXYZ( link.node2()));
6653       int i1 = outerFace->GetNodeIndex( link.node1() );
6654       int i2 = outerFace->GetNodeIndex( link.node2() );
6655       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6656       if ( rev ) n1n2.Reverse();
6657       // outerFace normal
6658       gp_XYZ ofNorm, fNorm;
6659       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6660       {
6661         // direction from the link inside outerFace
6662         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6663         // sort all other faces by angle with the dirInOF
6664         map< double, const SMDS_MeshElement* > angle2Face;
6665         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6666         for ( ; face != faces.end(); ++face )
6667         {
6668           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6669             continue;
6670           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6671           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6672           if ( angle < 0 ) angle += 2. * M_PI;
6673           angle2Face.insert( make_pair( angle, *face ));
6674         }
6675         if ( !angle2Face.empty() )
6676           outerFace2 = angle2Face.begin()->second;
6677       }
6678     }
6679     // store the found outer face and add its links to continue seaching from
6680     if ( outerFace2 )
6681     {
6682       _outerFaces.insert( outerFace );
6683       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6684       for ( int i = 0; i < nbNodes; ++i )
6685       {
6686         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6687         if ( visitedLinks.insert( link2 ).second )
6688           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6689       }
6690     }
6691     startLinks.pop_front();
6692   }
6693   _outerFacesFound = true;
6694
6695   if ( !seamLinks.empty() )
6696   {
6697     // There are internal boundaries touching the outher one,
6698     // find all faces of internal boundaries in order to find
6699     // faces of boundaries of holes, if any.
6700
6701   }
6702   else
6703   {
6704     _outerFaces.clear();
6705   }
6706 }
6707
6708 //=======================================================================
6709 /*!
6710  * \brief Find elements of given type where the given point is IN or ON.
6711  *        Returns nb of found elements and elements them-selves.
6712  *
6713  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6714  */
6715 //=======================================================================
6716
6717 int SMESH_ElementSearcherImpl::
6718 FindElementsByPoint(const gp_Pnt&                      point,
6719                     SMDSAbs_ElementType                type,
6720                     vector< const SMDS_MeshElement* >& foundElements)
6721 {
6722   foundElements.clear();
6723
6724   double tolerance = getTolerance();
6725
6726   // =================================================================================
6727   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6728   {
6729     if ( !_nodeSearcher )
6730       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6731
6732     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6733     if ( !closeNode ) return foundElements.size();
6734
6735     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6736       return foundElements.size(); // to far from any node
6737
6738     if ( type == SMDSAbs_Node )
6739     {
6740       foundElements.push_back( closeNode );
6741     }
6742     else
6743     {
6744       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6745       while ( elemIt->more() )
6746         foundElements.push_back( elemIt->next() );
6747     }
6748   }
6749   // =================================================================================
6750   else // elements more complex than 0D
6751   {
6752     if ( !_ebbTree || _elementType != type )
6753     {
6754       if ( _ebbTree ) delete _ebbTree;
6755       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6756     }
6757     TIDSortedElemSet suspectElems;
6758     _ebbTree->getElementsNearPoint( point, suspectElems );
6759     TIDSortedElemSet::iterator elem = suspectElems.begin();
6760     for ( ; elem != suspectElems.end(); ++elem )
6761       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6762         foundElements.push_back( *elem );
6763   }
6764   return foundElements.size();
6765 }
6766
6767 //=======================================================================
6768 /*!
6769  * \brief Find an element of given type most close to the given point
6770  *
6771  * WARNING: Only face search is implemeneted so far
6772  */
6773 //=======================================================================
6774
6775 const SMDS_MeshElement*
6776 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6777                                           SMDSAbs_ElementType type )
6778 {
6779   const SMDS_MeshElement* closestElem = 0;
6780
6781   if ( type == SMDSAbs_Face )
6782   {
6783     if ( !_ebbTree || _elementType != type )
6784     {
6785       if ( _ebbTree ) delete _ebbTree;
6786       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6787     }
6788     TIDSortedElemSet suspectElems;
6789     _ebbTree->getElementsNearPoint( point, suspectElems );
6790
6791     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6792     {
6793       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6794                                  _ebbTree->getBox()->CornerMax() );
6795       double radius;
6796       if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6797         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6798       else
6799         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6800       while ( suspectElems.empty() )
6801       {
6802         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6803         radius *= 1.1;
6804       }
6805     }
6806     double minDist = std::numeric_limits<double>::max();
6807     multimap< double, const SMDS_MeshElement* > dist2face;
6808     TIDSortedElemSet::iterator elem = suspectElems.begin();
6809     for ( ; elem != suspectElems.end(); ++elem )
6810     {
6811       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6812                                                    point );
6813       if ( dist < minDist + 1e-10)
6814       {
6815         minDist = dist;
6816         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6817       }
6818     }
6819     if ( !dist2face.empty() )
6820     {
6821       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6822       closestElem = d2f->second;
6823       // if there are several elements at the same distance, select one
6824       // with GC closest to the point
6825       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6826       double minDistToGC = 0;
6827       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6828       {
6829         if ( minDistToGC == 0 )
6830         {
6831           gp_XYZ gc(0,0,0);
6832           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6833                            TXyzIterator(), gc ) / closestElem->NbNodes();
6834           minDistToGC = point.SquareDistance( gc );
6835         }
6836         gp_XYZ gc(0,0,0);
6837         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6838                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6839         double d = point.SquareDistance( gc );
6840         if ( d < minDistToGC )
6841         {
6842           minDistToGC = d;
6843           closestElem = d2f->second;
6844         }
6845       }
6846       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6847       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6848     }
6849   }
6850   else
6851   {
6852     // NOT IMPLEMENTED SO FAR
6853   }
6854   return closestElem;
6855 }
6856
6857
6858 //================================================================================
6859 /*!
6860  * \brief Classify the given point in the closed 2D mesh
6861  */
6862 //================================================================================
6863
6864 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6865 {
6866   double tolerance = getTolerance();
6867   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6868   {
6869     if ( _ebbTree ) delete _ebbTree;
6870     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6871   }
6872   // Algo: analyse transition of a line starting at the point through mesh boundary;
6873   // try three lines parallel to axis of the coordinate system and perform rough
6874   // analysis. If solution is not clear perform thorough analysis.
6875
6876   const int nbAxes = 3;
6877   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6878   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6879   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6880   multimap< int, int > nbInt2Axis; // to find the simplest case
6881   for ( int axis = 0; axis < nbAxes; ++axis )
6882   {
6883     gp_Ax1 lineAxis( point, axisDir[axis]);
6884     gp_Lin line    ( lineAxis );
6885
6886     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6887     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6888
6889     // Intersect faces with the line
6890
6891     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6892     TIDSortedElemSet::iterator face = suspectFaces.begin();
6893     for ( ; face != suspectFaces.end(); ++face )
6894     {
6895       // get face plane
6896       gp_XYZ fNorm;
6897       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6898       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6899
6900       // perform intersection
6901       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6902       if ( !intersection.IsDone() )
6903         continue;
6904       if ( intersection.IsInQuadric() )
6905       {
6906         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6907       }
6908       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6909       {
6910         gp_Pnt intersectionPoint = intersection.Point(1);
6911         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6912           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6913       }
6914     }
6915     // Analyse intersections roughly
6916
6917     int nbInter = u2inters.size();
6918     if ( nbInter == 0 )
6919       return TopAbs_OUT;
6920
6921     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6922     if ( nbInter == 1 ) // not closed mesh
6923       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6924
6925     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6926       return TopAbs_ON;
6927
6928     if ( (f<0) == (l<0) )
6929       return TopAbs_OUT;
6930
6931     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6932     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
6933     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6934       return TopAbs_IN;
6935
6936     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6937
6938     if ( _outerFacesFound ) break; // pass to thorough analysis
6939
6940   } // three attempts - loop on CS axes
6941
6942   // Analyse intersections thoroughly.
6943   // We make two loops maximum, on the first one we only exclude touching intersections,
6944   // on the second, if situation is still unclear, we gather and use information on
6945   // position of faces (internal or outer). If faces position is already gathered,
6946   // we make the second loop right away.
6947
6948   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6949   {
6950     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6951     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6952     {
6953       int axis = nb_axis->second;
6954       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6955
6956       gp_Ax1 lineAxis( point, axisDir[axis]);
6957       gp_Lin line    ( lineAxis );
6958
6959       // add tangent intersections to u2inters
6960       double param;
6961       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6962       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6963         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6964           u2inters.insert(make_pair( param, *tgtInt ));
6965       tangentInters[ axis ].clear();
6966
6967       // Count intersections before and after the point excluding touching ones.
6968       // If hasPositionInfo we count intersections of outer boundary only
6969
6970       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6971       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6972       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6973       bool ok = ! u_int1->second._coincides;
6974       while ( ok && u_int1 != u2inters.end() )
6975       {
6976         double u = u_int1->first;
6977         bool touchingInt = false;
6978         if ( ++u_int2 != u2inters.end() )
6979         {
6980           // skip intersections at the same point (if the line passes through edge or node)
6981           int nbSamePnt = 0;
6982           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6983           {
6984             ++nbSamePnt;
6985             ++u_int2;
6986           }
6987
6988           // skip tangent intersections
6989           int nbTgt = 0;
6990           const SMDS_MeshElement* prevFace = u_int1->second._face;
6991           while ( ok && u_int2->second._coincides )
6992           {
6993             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6994               ok = false;
6995             else
6996             {
6997               nbTgt++;
6998               u_int2++;
6999               ok = ( u_int2 != u2inters.end() );
7000             }
7001           }
7002           if ( !ok ) break;
7003
7004           // skip intersections at the same point after tangent intersections
7005           if ( nbTgt > 0 )
7006           {
7007             double u2 = u_int2->first;
7008             ++u_int2;
7009             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7010             {
7011               ++nbSamePnt;
7012               ++u_int2;
7013             }
7014           }
7015           // decide if we skipped a touching intersection
7016           if ( nbSamePnt + nbTgt > 0 )
7017           {
7018             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7019             map< double, TInters >::iterator u_int = u_int1;
7020             for ( ; u_int != u_int2; ++u_int )
7021             {
7022               if ( u_int->second._coincides ) continue;
7023               double dot = u_int->second._faceNorm * line.Direction();
7024               if ( dot > maxDot ) maxDot = dot;
7025               if ( dot < minDot ) minDot = dot;
7026             }
7027             touchingInt = ( minDot*maxDot < 0 );
7028           }
7029         }
7030         if ( !touchingInt )
7031         {
7032           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7033           {
7034             if ( u < 0 )
7035               ++nbIntBeforePoint;
7036             else
7037               ++nbIntAfterPoint;
7038           }
7039           if ( u < f ) f = u;
7040           if ( u > l ) l = u;
7041         }
7042
7043         u_int1 = u_int2; // to next intersection
7044
7045       } // loop on intersections with one line
7046
7047       if ( ok )
7048       {
7049         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7050           return TopAbs_ON;
7051
7052         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
7053           return TopAbs_OUT;
7054
7055         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7056           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7057
7058         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7059           return TopAbs_IN;
7060
7061         if ( (f<0) == (l<0) )
7062           return TopAbs_OUT;
7063
7064         if ( hasPositionInfo )
7065           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7066       }
7067     } // loop on intersections of the tree lines - thorough analysis
7068
7069     if ( !hasPositionInfo )
7070     {
7071       // gather info on faces position - is face in the outer boundary or not
7072       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7073       findOuterBoundary( u2inters.begin()->second._face );
7074     }
7075
7076   } // two attempts - with and w/o faces position info in the mesh
7077
7078   return TopAbs_UNKNOWN;
7079 }
7080
7081 //=======================================================================
7082 /*!
7083  * \brief Return elements possibly intersecting the line
7084  */
7085 //=======================================================================
7086
7087 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7088                                                      SMDSAbs_ElementType                type,
7089                                                      vector< const SMDS_MeshElement* >& foundElems)
7090 {
7091   if ( !_ebbTree || _elementType != type )
7092   {
7093     if ( _ebbTree ) delete _ebbTree;
7094     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7095   }
7096   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7097   _ebbTree->getElementsNearLine( line, suspectFaces );
7098   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7099 }
7100
7101 //=======================================================================
7102 /*!
7103  * \brief Return SMESH_ElementSearcher
7104  */
7105 //=======================================================================
7106
7107 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7108 {
7109   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7110 }
7111
7112 //=======================================================================
7113 /*!
7114  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7115  */
7116 //=======================================================================
7117
7118 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7119 {
7120   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7121 }
7122
7123 //=======================================================================
7124 /*!
7125  * \brief Return true if the point is IN or ON of the element
7126  */
7127 //=======================================================================
7128
7129 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7130 {
7131   if ( element->GetType() == SMDSAbs_Volume)
7132   {
7133     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7134   }
7135
7136   // get ordered nodes
7137
7138   vector< gp_XYZ > xyz;
7139   vector<const SMDS_MeshNode*> nodeList;
7140
7141   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7142   if ( element->IsQuadratic() ) {
7143     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7144       nodeIt = f->interlacedNodesElemIterator();
7145     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7146       nodeIt = e->interlacedNodesElemIterator();
7147   }
7148   while ( nodeIt->more() )
7149     {
7150       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7151       xyz.push_back( SMESH_TNodeXYZ(node) );
7152       nodeList.push_back(node);
7153     }
7154
7155   int i, nbNodes = element->NbNodes();
7156
7157   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7158   {
7159     // compute face normal
7160     gp_Vec faceNorm(0,0,0);
7161     xyz.push_back( xyz.front() );
7162     nodeList.push_back( nodeList.front() );
7163     for ( i = 0; i < nbNodes; ++i )
7164     {
7165       gp_Vec edge1( xyz[i+1], xyz[i]);
7166       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7167       faceNorm += edge1 ^ edge2;
7168     }
7169     double normSize = faceNorm.Magnitude();
7170     if ( normSize <= tol )
7171     {
7172       // degenerated face: point is out if it is out of all face edges
7173       for ( i = 0; i < nbNodes; ++i )
7174       {
7175         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7176         if ( !IsOut( &edge, point, tol ))
7177           return false;
7178       }
7179       return true;
7180     }
7181     faceNorm /= normSize;
7182
7183     // check if the point lays on face plane
7184     gp_Vec n2p( xyz[0], point );
7185     if ( fabs( n2p * faceNorm ) > tol )
7186       return true; // not on face plane
7187
7188     // check if point is out of face boundary:
7189     // define it by closest transition of a ray point->infinity through face boundary
7190     // on the face plane.
7191     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7192     // to find intersections of the ray with the boundary.
7193     gp_Vec ray = n2p;
7194     gp_Vec plnNorm = ray ^ faceNorm;
7195     normSize = plnNorm.Magnitude();
7196     if ( normSize <= tol ) return false; // point coincides with the first node
7197     plnNorm /= normSize;
7198     // for each node of the face, compute its signed distance to the plane
7199     vector<double> dist( nbNodes + 1);
7200     for ( i = 0; i < nbNodes; ++i )
7201     {
7202       gp_Vec n2p( xyz[i], point );
7203       dist[i] = n2p * plnNorm;
7204     }
7205     dist.back() = dist.front();
7206     // find the closest intersection
7207     int    iClosest = -1;
7208     double rClosest, distClosest = 1e100;;
7209     gp_Pnt pClosest;
7210     for ( i = 0; i < nbNodes; ++i )
7211     {
7212       double r;
7213       if ( fabs( dist[i]) < tol )
7214         r = 0.;
7215       else if ( fabs( dist[i+1]) < tol )
7216         r = 1.;
7217       else if ( dist[i] * dist[i+1] < 0 )
7218         r = dist[i] / ( dist[i] - dist[i+1] );
7219       else
7220         continue; // no intersection
7221       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7222       gp_Vec p2int ( point, pInt);
7223       if ( p2int * ray > -tol ) // right half-space
7224       {
7225         double intDist = p2int.SquareMagnitude();
7226         if ( intDist < distClosest )
7227         {
7228           iClosest = i;
7229           rClosest = r;
7230           pClosest = pInt;
7231           distClosest = intDist;
7232         }
7233       }
7234     }
7235     if ( iClosest < 0 )
7236       return true; // no intesections - out
7237
7238     // analyse transition
7239     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7240     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7241     gp_Vec p2int ( point, pClosest );
7242     bool out = (edgeNorm * p2int) < -tol;
7243     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7244       return out;
7245
7246     // ray pass through a face node; analyze transition through an adjacent edge
7247     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7248     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7249     gp_Vec edgeAdjacent( p1, p2 );
7250     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7251     bool out2 = (edgeNorm2 * p2int) < -tol;
7252
7253     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7254     return covexCorner ? (out || out2) : (out && out2);
7255   }
7256   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7257   {
7258     // point is out of edge if it is NOT ON any straight part of edge
7259     // (we consider quadratic edge as being composed of two straight parts)
7260     for ( i = 1; i < nbNodes; ++i )
7261     {
7262       gp_Vec edge( xyz[i-1], xyz[i]);
7263       gp_Vec n1p ( xyz[i-1], point);
7264       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7265       if ( dist > tol )
7266         continue;
7267       gp_Vec n2p( xyz[i], point );
7268       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7269         continue;
7270       return false; // point is ON this part
7271     }
7272     return true;
7273   }
7274   // Node or 0D element -------------------------------------------------------------------------
7275   {
7276     gp_Vec n2p ( xyz[0], point );
7277     return n2p.Magnitude() <= tol;
7278   }
7279   return true;
7280 }
7281
7282 //=======================================================================
7283
7284 namespace
7285 {
7286   // Position of a point relative to a segment
7287   //            .           .
7288   //            .  LEFT     .
7289   //            .           .
7290   //  VERTEX 1  o----ON----->  VERTEX 2
7291   //            .           .
7292   //            .  RIGHT    .
7293   //            .           .
7294   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7295                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7296   struct PointPos
7297   {
7298     PositionName _name;
7299     int          _index; // index of vertex or segment
7300
7301     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7302     bool operator < (const PointPos& other ) const
7303     {
7304       if ( _name == other._name )
7305         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7306       return _name < other._name;
7307     }
7308   };
7309
7310   //================================================================================
7311   /*!
7312    * \brief Return of a point relative to a segment
7313    *  \param point2D      - the point to analyze position of
7314    *  \param xyVec        - end points of segments
7315    *  \param index0       - 0-based index of the first point of segment
7316    *  \param posToFindOut - flags of positions to detect
7317    *  \retval PointPos - point position
7318    */
7319   //================================================================================
7320
7321   PointPos getPointPosition( const gp_XY& point2D,
7322                              const gp_XY* segEnds,
7323                              const int    index0 = 0,
7324                              const int    posToFindOut = POS_ALL)
7325   {
7326     const gp_XY& p1 = segEnds[ index0   ];
7327     const gp_XY& p2 = segEnds[ index0+1 ];
7328     const gp_XY grad = p2 - p1;
7329
7330     if ( posToFindOut & POS_VERTEX )
7331     {
7332       // check if the point2D is at "vertex 1" zone
7333       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7334                                   p1.Y() + grad.X() ) };
7335       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7336         return PointPos( POS_VERTEX, index0 );
7337
7338       // check if the point2D is at "vertex 2" zone
7339       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7340                                   p2.Y() + grad.X() ) };
7341       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7342         return PointPos( POS_VERTEX, index0 + 1);
7343     }
7344     double edgeEquation =
7345       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7346     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7347   }
7348 }
7349
7350 //=======================================================================
7351 /*!
7352  * \brief Return minimal distance from a point to a face
7353  *
7354  * Currently we ignore non-planarity and 2nd order of face
7355  */
7356 //=======================================================================
7357
7358 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7359                                       const gp_Pnt&        point )
7360 {
7361   double badDistance = -1;
7362   if ( !face ) return badDistance;
7363
7364   // coordinates of nodes (medium nodes, if any, ignored)
7365   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7366   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7367   xyz.resize( face->NbCornerNodes()+1 );
7368
7369   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7370   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7371   gp_Trsf trsf;
7372   gp_Vec OZ ( xyz[0], xyz[1] );
7373   gp_Vec OX ( xyz[0], xyz[2] );
7374   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7375   {
7376     if ( xyz.size() < 4 ) return badDistance;
7377     OZ = gp_Vec ( xyz[0], xyz[2] );
7378     OX = gp_Vec ( xyz[0], xyz[3] );
7379   }
7380   gp_Ax3 tgtCS;
7381   try {
7382     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7383   }
7384   catch ( Standard_Failure ) {
7385     return badDistance;
7386   }
7387   trsf.SetTransformation( tgtCS );
7388
7389   // move all the nodes to 2D
7390   vector<gp_XY> xy( xyz.size() );
7391   for ( size_t i = 0;i < xyz.size()-1; ++i )
7392   {
7393     gp_XYZ p3d = xyz[i];
7394     trsf.Transforms( p3d );
7395     xy[i].SetCoord( p3d.X(), p3d.Z() );
7396   }
7397   xyz.back() = xyz.front();
7398   xy.back() = xy.front();
7399
7400   // // move the point in 2D
7401   gp_XYZ tmpPnt = point.XYZ();
7402   trsf.Transforms( tmpPnt );
7403   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7404
7405   // loop on segments of the face to analyze point position ralative to the face
7406   set< PointPos > pntPosSet;
7407   for ( size_t i = 1; i < xy.size(); ++i )
7408   {
7409     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7410     pntPosSet.insert( pos );
7411   }
7412
7413   // compute distance
7414   PointPos pos = *pntPosSet.begin();
7415   // cout << "Face " << face->GetID() << " DIST: ";
7416   switch ( pos._name )
7417   {
7418   case POS_LEFT: {
7419     // point is most close to a segment
7420     gp_Vec p0p1( point, xyz[ pos._index ] );
7421     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7422     p1p2.Normalize();
7423     double projDist = p0p1 * p1p2; // distance projected to the segment
7424     gp_Vec projVec = p1p2 * projDist;
7425     gp_Vec distVec = p0p1 - projVec;
7426     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7427     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7428     return distVec.Magnitude();
7429   }
7430   case POS_RIGHT: {
7431     // point is inside the face
7432     double distToFacePlane = tmpPnt.Y();
7433     // cout << distToFacePlane << ", INSIDE " << endl;
7434     return Abs( distToFacePlane );
7435   }
7436   case POS_VERTEX: {
7437     // point is most close to a node
7438     gp_Vec distVec( point, xyz[ pos._index ]);
7439     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7440     return distVec.Magnitude();
7441   }
7442   }
7443   return badDistance;
7444 }
7445
7446 //=======================================================================
7447 //function : SimplifyFace
7448 //purpose  :
7449 //=======================================================================
7450 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7451                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7452                                     vector<int>&                        quantities) const
7453 {
7454   int nbNodes = faceNodes.size();
7455
7456   if (nbNodes < 3)
7457     return 0;
7458
7459   set<const SMDS_MeshNode*> nodeSet;
7460
7461   // get simple seq of nodes
7462   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7463   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7464   int iSimple = 0, nbUnique = 0;
7465
7466   simpleNodes[iSimple++] = faceNodes[0];
7467   nbUnique++;
7468   for (int iCur = 1; iCur < nbNodes; iCur++) {
7469     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7470       simpleNodes[iSimple++] = faceNodes[iCur];
7471       if (nodeSet.insert( faceNodes[iCur] ).second)
7472         nbUnique++;
7473     }
7474   }
7475   int nbSimple = iSimple;
7476   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7477     nbSimple--;
7478     iSimple--;
7479   }
7480
7481   if (nbUnique < 3)
7482     return 0;
7483
7484   // separate loops
7485   int nbNew = 0;
7486   bool foundLoop = (nbSimple > nbUnique);
7487   while (foundLoop) {
7488     foundLoop = false;
7489     set<const SMDS_MeshNode*> loopSet;
7490     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7491       const SMDS_MeshNode* n = simpleNodes[iSimple];
7492       if (!loopSet.insert( n ).second) {
7493         foundLoop = true;
7494
7495         // separate loop
7496         int iC = 0, curLast = iSimple;
7497         for (; iC < curLast; iC++) {
7498           if (simpleNodes[iC] == n) break;
7499         }
7500         int loopLen = curLast - iC;
7501         if (loopLen > 2) {
7502           // create sub-element
7503           nbNew++;
7504           quantities.push_back(loopLen);
7505           for (; iC < curLast; iC++) {
7506             poly_nodes.push_back(simpleNodes[iC]);
7507           }
7508         }
7509         // shift the rest nodes (place from the first loop position)
7510         for (iC = curLast + 1; iC < nbSimple; iC++) {
7511           simpleNodes[iC - loopLen] = simpleNodes[iC];
7512         }
7513         nbSimple -= loopLen;
7514         iSimple -= loopLen;
7515       }
7516     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7517   } // while (foundLoop)
7518
7519   if (iSimple > 2) {
7520     nbNew++;
7521     quantities.push_back(iSimple);
7522     for (int i = 0; i < iSimple; i++)
7523       poly_nodes.push_back(simpleNodes[i]);
7524   }
7525
7526   return nbNew;
7527 }
7528
7529 //=======================================================================
7530 //function : MergeNodes
7531 //purpose  : In each group, the cdr of nodes are substituted by the first one
7532 //           in all elements.
7533 //=======================================================================
7534
7535 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7536 {
7537   MESSAGE("MergeNodes");
7538   myLastCreatedElems.Clear();
7539   myLastCreatedNodes.Clear();
7540
7541   SMESHDS_Mesh* aMesh = GetMeshDS();
7542
7543   TNodeNodeMap nodeNodeMap; // node to replace - new node
7544   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7545   list< int > rmElemIds, rmNodeIds;
7546
7547   // Fill nodeNodeMap and elems
7548
7549   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7550   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7551     list<const SMDS_MeshNode*>& nodes = *grIt;
7552     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7553     const SMDS_MeshNode* nToKeep = *nIt;
7554     //MESSAGE("node to keep " << nToKeep->GetID());
7555     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7556       const SMDS_MeshNode* nToRemove = *nIt;
7557       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7558       if ( nToRemove != nToKeep ) {
7559         //MESSAGE("  node to remove " << nToRemove->GetID());
7560         rmNodeIds.push_back( nToRemove->GetID() );
7561         AddToSameGroups( nToKeep, nToRemove, aMesh );
7562       }
7563
7564       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7565       while ( invElemIt->more() ) {
7566         const SMDS_MeshElement* elem = invElemIt->next();
7567         elems.insert(elem);
7568       }
7569     }
7570   }
7571   // Change element nodes or remove an element
7572
7573   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7574   for ( ; eIt != elems.end(); eIt++ ) {
7575     const SMDS_MeshElement* elem = *eIt;
7576     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7577     int nbNodes = elem->NbNodes();
7578     int aShapeId = FindShape( elem );
7579
7580     set<const SMDS_MeshNode*> nodeSet;
7581     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7582     int iUnique = 0, iCur = 0, nbRepl = 0;
7583     vector<int> iRepl( nbNodes );
7584
7585     // get new seq of nodes
7586     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7587     while ( itN->more() ) {
7588       const SMDS_MeshNode* n =
7589         static_cast<const SMDS_MeshNode*>( itN->next() );
7590
7591       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7592       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7593         n = (*nnIt).second;
7594         // BUG 0020185: begin
7595         {
7596           bool stopRecur = false;
7597           set<const SMDS_MeshNode*> nodesRecur;
7598           nodesRecur.insert(n);
7599           while (!stopRecur) {
7600             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7601             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7602               n = (*nnIt_i).second;
7603               if (!nodesRecur.insert(n).second) {
7604                 // error: recursive dependancy
7605                 stopRecur = true;
7606               }
7607             }
7608             else
7609               stopRecur = true;
7610           }
7611         }
7612         // BUG 0020185: end
7613       }
7614       curNodes[ iCur ] = n;
7615       bool isUnique = nodeSet.insert( n ).second;
7616       if ( isUnique )
7617         uniqueNodes[ iUnique++ ] = n;
7618       else
7619         iRepl[ nbRepl++ ] = iCur;
7620       iCur++;
7621     }
7622
7623     // Analyse element topology after replacement
7624
7625     bool isOk = true;
7626     int nbUniqueNodes = nodeSet.size();
7627     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7628     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7629       // Polygons and Polyhedral volumes
7630       if (elem->IsPoly()) {
7631
7632         if (elem->GetType() == SMDSAbs_Face) {
7633           // Polygon
7634           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7635           int inode = 0;
7636           for (; inode < nbNodes; inode++) {
7637             face_nodes[inode] = curNodes[inode];
7638           }
7639
7640           vector<const SMDS_MeshNode *> polygons_nodes;
7641           vector<int> quantities;
7642           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7643           if (nbNew > 0) {
7644             inode = 0;
7645             for (int iface = 0; iface < nbNew; iface++) {
7646               int nbNodes = quantities[iface];
7647               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7648               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7649                 poly_nodes[ii] = polygons_nodes[inode];
7650               }
7651               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7652               myLastCreatedElems.Append(newElem);
7653               if (aShapeId)
7654                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7655             }
7656
7657             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7658             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7659             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7660             int quid =0;
7661             if (nbNew > 0) quid = nbNew - 1;
7662             vector<int> newquant(quantities.begin()+quid, quantities.end());
7663             const SMDS_MeshElement* newElem = 0;
7664             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7665             myLastCreatedElems.Append(newElem);
7666             if ( aShapeId && newElem )
7667               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7668             rmElemIds.push_back(elem->GetID());
7669           }
7670           else {
7671             rmElemIds.push_back(elem->GetID());
7672           }
7673
7674         }
7675         else if (elem->GetType() == SMDSAbs_Volume) {
7676           // Polyhedral volume
7677           if (nbUniqueNodes < 4) {
7678             rmElemIds.push_back(elem->GetID());
7679           }
7680           else {
7681             // each face has to be analyzed in order to check volume validity
7682             const SMDS_VtkVolume* aPolyedre =
7683               dynamic_cast<const SMDS_VtkVolume*>( elem );
7684             if (aPolyedre) {
7685               int nbFaces = aPolyedre->NbFaces();
7686
7687               vector<const SMDS_MeshNode *> poly_nodes;
7688               vector<int> quantities;
7689
7690               for (int iface = 1; iface <= nbFaces; iface++) {
7691                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7692                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7693
7694                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7695                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7696                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7697                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7698                     faceNode = (*nnIt).second;
7699                   }
7700                   faceNodes[inode - 1] = faceNode;
7701                 }
7702
7703                 SimplifyFace(faceNodes, poly_nodes, quantities);
7704               }
7705
7706               if (quantities.size() > 3) {
7707                 // to be done: remove coincident faces
7708               }
7709
7710               if (quantities.size() > 3)
7711                 {
7712                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7713                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7714                   const SMDS_MeshElement* newElem = 0;
7715                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7716                   myLastCreatedElems.Append(newElem);
7717                   if ( aShapeId && newElem )
7718                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7719                   rmElemIds.push_back(elem->GetID());
7720                 }
7721             }
7722             else {
7723               rmElemIds.push_back(elem->GetID());
7724             }
7725           }
7726         }
7727         else {
7728         }
7729
7730         continue;
7731       } // poly element
7732
7733       // Regular elements
7734       // TODO not all the possible cases are solved. Find something more generic?
7735       switch ( nbNodes ) {
7736       case 2: ///////////////////////////////////// EDGE
7737         isOk = false; break;
7738       case 3: ///////////////////////////////////// TRIANGLE
7739         isOk = false; break;
7740       case 4:
7741         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7742           isOk = false;
7743         else { //////////////////////////////////// QUADRANGLE
7744           if ( nbUniqueNodes < 3 )
7745             isOk = false;
7746           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7747             isOk = false; // opposite nodes stick
7748           //MESSAGE("isOk " << isOk);
7749         }
7750         break;
7751       case 6: ///////////////////////////////////// PENTAHEDRON
7752         if ( nbUniqueNodes == 4 ) {
7753           // ---------------------------------> tetrahedron
7754           if (nbRepl == 3 &&
7755               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7756             // all top nodes stick: reverse a bottom
7757             uniqueNodes[ 0 ] = curNodes [ 1 ];
7758             uniqueNodes[ 1 ] = curNodes [ 0 ];
7759           }
7760           else if (nbRepl == 3 &&
7761                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7762             // all bottom nodes stick: set a top before
7763             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7764             uniqueNodes[ 0 ] = curNodes [ 3 ];
7765             uniqueNodes[ 1 ] = curNodes [ 4 ];
7766             uniqueNodes[ 2 ] = curNodes [ 5 ];
7767           }
7768           else if (nbRepl == 4 &&
7769                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7770             // a lateral face turns into a line: reverse a bottom
7771             uniqueNodes[ 0 ] = curNodes [ 1 ];
7772             uniqueNodes[ 1 ] = curNodes [ 0 ];
7773           }
7774           else
7775             isOk = false;
7776         }
7777         else if ( nbUniqueNodes == 5 ) {
7778           // PENTAHEDRON --------------------> 2 tetrahedrons
7779           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7780             // a bottom node sticks with a linked top one
7781             // 1.
7782             SMDS_MeshElement* newElem =
7783               aMesh->AddVolume(curNodes[ 3 ],
7784                                curNodes[ 4 ],
7785                                curNodes[ 5 ],
7786                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7787             myLastCreatedElems.Append(newElem);
7788             if ( aShapeId )
7789               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7790             // 2. : reverse a bottom
7791             uniqueNodes[ 0 ] = curNodes [ 1 ];
7792             uniqueNodes[ 1 ] = curNodes [ 0 ];
7793             nbUniqueNodes = 4;
7794           }
7795           else
7796             isOk = false;
7797         }
7798         else
7799           isOk = false;
7800         break;
7801       case 8: {
7802         if(elem->IsQuadratic()) { // Quadratic quadrangle
7803           //   1    5    2
7804           //    +---+---+
7805           //    |       |
7806           //    |       |
7807           //   4+       +6
7808           //    |       |
7809           //    |       |
7810           //    +---+---+
7811           //   0    7    3
7812           isOk = false;
7813           if(nbRepl==2) {
7814             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7815           }
7816           if(nbRepl==3) {
7817             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7818             nbUniqueNodes = 6;
7819             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7820               uniqueNodes[0] = curNodes[0];
7821               uniqueNodes[1] = curNodes[2];
7822               uniqueNodes[2] = curNodes[3];
7823               uniqueNodes[3] = curNodes[5];
7824               uniqueNodes[4] = curNodes[6];
7825               uniqueNodes[5] = curNodes[7];
7826               isOk = true;
7827             }
7828             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7829               uniqueNodes[0] = curNodes[0];
7830               uniqueNodes[1] = curNodes[1];
7831               uniqueNodes[2] = curNodes[2];
7832               uniqueNodes[3] = curNodes[4];
7833               uniqueNodes[4] = curNodes[5];
7834               uniqueNodes[5] = curNodes[6];
7835               isOk = true;
7836             }
7837             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7838               uniqueNodes[0] = curNodes[1];
7839               uniqueNodes[1] = curNodes[2];
7840               uniqueNodes[2] = curNodes[3];
7841               uniqueNodes[3] = curNodes[5];
7842               uniqueNodes[4] = curNodes[6];
7843               uniqueNodes[5] = curNodes[0];
7844               isOk = true;
7845             }
7846             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7847               uniqueNodes[0] = curNodes[0];
7848               uniqueNodes[1] = curNodes[1];
7849               uniqueNodes[2] = curNodes[3];
7850               uniqueNodes[3] = curNodes[4];
7851               uniqueNodes[4] = curNodes[6];
7852               uniqueNodes[5] = curNodes[7];
7853               isOk = true;
7854             }
7855             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7856               uniqueNodes[0] = curNodes[0];
7857               uniqueNodes[1] = curNodes[2];
7858               uniqueNodes[2] = curNodes[3];
7859               uniqueNodes[3] = curNodes[1];
7860               uniqueNodes[4] = curNodes[6];
7861               uniqueNodes[5] = curNodes[7];
7862               isOk = true;
7863             }
7864             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7865               uniqueNodes[0] = curNodes[0];
7866               uniqueNodes[1] = curNodes[1];
7867               uniqueNodes[2] = curNodes[2];
7868               uniqueNodes[3] = curNodes[4];
7869               uniqueNodes[4] = curNodes[5];
7870               uniqueNodes[5] = curNodes[7];
7871               isOk = true;
7872             }
7873             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7874               uniqueNodes[0] = curNodes[0];
7875               uniqueNodes[1] = curNodes[1];
7876               uniqueNodes[2] = curNodes[3];
7877               uniqueNodes[3] = curNodes[4];
7878               uniqueNodes[4] = curNodes[2];
7879               uniqueNodes[5] = curNodes[7];
7880               isOk = true;
7881             }
7882             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7883               uniqueNodes[0] = curNodes[0];
7884               uniqueNodes[1] = curNodes[1];
7885               uniqueNodes[2] = curNodes[2];
7886               uniqueNodes[3] = curNodes[4];
7887               uniqueNodes[4] = curNodes[5];
7888               uniqueNodes[5] = curNodes[3];
7889               isOk = true;
7890             }
7891           }
7892           if(nbRepl==4) {
7893             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7894           }
7895           if(nbRepl==5) {
7896             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7897           }
7898           break;
7899         }
7900         //////////////////////////////////// HEXAHEDRON
7901         isOk = false;
7902         SMDS_VolumeTool hexa (elem);
7903         hexa.SetExternalNormal();
7904         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7905           //////////////////////// HEX ---> 1 tetrahedron
7906           for ( int iFace = 0; iFace < 6; iFace++ ) {
7907             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7908             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7909                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7910                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7911               // one face turns into a point ...
7912               int iOppFace = hexa.GetOppFaceIndex( iFace );
7913               ind = hexa.GetFaceNodesIndices( iOppFace );
7914               int nbStick = 0;
7915               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7916                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7917                   nbStick++;
7918               }
7919               if ( nbStick == 1 ) {
7920                 // ... and the opposite one - into a triangle.
7921                 // set a top node
7922                 ind = hexa.GetFaceNodesIndices( iFace );
7923                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7924                 isOk = true;
7925               }
7926               break;
7927             }
7928           }
7929         }
7930         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7931           //////////////////////// HEX ---> 1 prism
7932           int nbTria = 0, iTria[3];
7933           const int *ind; // indices of face nodes
7934           // look for triangular faces
7935           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7936             ind = hexa.GetFaceNodesIndices( iFace );
7937             TIDSortedNodeSet faceNodes;
7938             for ( iCur = 0; iCur < 4; iCur++ )
7939               faceNodes.insert( curNodes[ind[iCur]] );
7940             if ( faceNodes.size() == 3 )
7941               iTria[ nbTria++ ] = iFace;
7942           }
7943           // check if triangles are opposite
7944           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7945           {
7946             isOk = true;
7947             // set nodes of the bottom triangle
7948             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7949             vector<int> indB;
7950             for ( iCur = 0; iCur < 4; iCur++ )
7951               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7952                 indB.push_back( ind[iCur] );
7953             if ( !hexa.IsForward() )
7954               std::swap( indB[0], indB[2] );
7955             for ( iCur = 0; iCur < 3; iCur++ )
7956               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7957             // set nodes of the top triangle
7958             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7959             for ( iCur = 0; iCur < 3; ++iCur )
7960               for ( int j = 0; j < 4; ++j )
7961                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7962                 {
7963                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7964                   break;
7965                 }
7966           }
7967           break;
7968         }
7969         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7970           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7971           for ( int iFace = 0; iFace < 6; iFace++ ) {
7972             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7973             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7974                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7975                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7976               // one face turns into a point ...
7977               int iOppFace = hexa.GetOppFaceIndex( iFace );
7978               ind = hexa.GetFaceNodesIndices( iOppFace );
7979               int nbStick = 0;
7980               iUnique = 2;  // reverse a tetrahedron 1 bottom
7981               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7982                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7983                   nbStick++;
7984                 else if ( iUnique >= 0 )
7985                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7986               }
7987               if ( nbStick == 0 ) {
7988                 // ... and the opposite one is a quadrangle
7989                 // set a top node
7990                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7991                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7992                 nbUniqueNodes = 4;
7993                 // tetrahedron 2
7994                 SMDS_MeshElement* newElem =
7995                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7996                                    curNodes[ind[ 3 ]],
7997                                    curNodes[ind[ 2 ]],
7998                                    curNodes[indTop[ 0 ]]);
7999                 myLastCreatedElems.Append(newElem);
8000                 if ( aShapeId )
8001                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8002                 isOk = true;
8003               }
8004               break;
8005             }
8006           }
8007         }
8008         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8009           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8010           // find indices of quad and tri faces
8011           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8012           for ( iFace = 0; iFace < 6; iFace++ ) {
8013             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8014             nodeSet.clear();
8015             for ( iCur = 0; iCur < 4; iCur++ )
8016               nodeSet.insert( curNodes[ind[ iCur ]] );
8017             nbUniqueNodes = nodeSet.size();
8018             if ( nbUniqueNodes == 3 )
8019               iTriFace[ nbTri++ ] = iFace;
8020             else if ( nbUniqueNodes == 4 )
8021               iQuadFace[ nbQuad++ ] = iFace;
8022           }
8023           if (nbQuad == 2 && nbTri == 4 &&
8024               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8025             // 2 opposite quadrangles stuck with a diagonal;
8026             // sample groups of merged indices: (0-4)(2-6)
8027             // --------------------------------------------> 2 tetrahedrons
8028             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8029             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8030             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8031             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8032                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8033               // stuck with 0-2 diagonal
8034               i0  = ind1[ 3 ];
8035               i1d = ind1[ 0 ];
8036               i2  = ind1[ 1 ];
8037               i3d = ind1[ 2 ];
8038               i0t = ind2[ 1 ];
8039               i2t = ind2[ 3 ];
8040             }
8041             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8042                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8043               // stuck with 1-3 diagonal
8044               i0  = ind1[ 0 ];
8045               i1d = ind1[ 1 ];
8046               i2  = ind1[ 2 ];
8047               i3d = ind1[ 3 ];
8048               i0t = ind2[ 0 ];
8049               i2t = ind2[ 1 ];
8050             }
8051             else {
8052               ASSERT(0);
8053             }
8054             // tetrahedron 1
8055             uniqueNodes[ 0 ] = curNodes [ i0 ];
8056             uniqueNodes[ 1 ] = curNodes [ i1d ];
8057             uniqueNodes[ 2 ] = curNodes [ i3d ];
8058             uniqueNodes[ 3 ] = curNodes [ i0t ];
8059             nbUniqueNodes = 4;
8060             // tetrahedron 2
8061             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8062                                                          curNodes[ i2 ],
8063                                                          curNodes[ i3d ],
8064                                                          curNodes[ i2t ]);
8065             myLastCreatedElems.Append(newElem);
8066             if ( aShapeId )
8067               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8068             isOk = true;
8069           }
8070           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8071                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8072             // --------------------------------------------> prism
8073             // find 2 opposite triangles
8074             nbUniqueNodes = 6;
8075             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8076               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8077                 // find indices of kept and replaced nodes
8078                 // and fill unique nodes of 2 opposite triangles
8079                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8080                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8081                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8082                 // fill unique nodes
8083                 iUnique = 0;
8084                 isOk = true;
8085                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8086                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8087                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8088                   if ( n == nInit ) {
8089                     // iCur of a linked node of the opposite face (make normals co-directed):
8090                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8091                     // check that correspondent corners of triangles are linked
8092                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8093                       isOk = false;
8094                     else {
8095                       uniqueNodes[ iUnique ] = n;
8096                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8097                       iUnique++;
8098                     }
8099                   }
8100                 }
8101                 break;
8102               }
8103             }
8104           }
8105         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8106         else
8107         {
8108           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8109         }
8110         break;
8111       } // HEXAHEDRON
8112
8113       default:
8114         isOk = false;
8115       } // switch ( nbNodes )
8116
8117     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8118
8119     if ( isOk ) { // the elem remains valid after sticking nodes
8120       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8121       {
8122         // Change nodes of polyedre
8123         const SMDS_VtkVolume* aPolyedre =
8124           dynamic_cast<const SMDS_VtkVolume*>( elem );
8125         if (aPolyedre) {
8126           int nbFaces = aPolyedre->NbFaces();
8127
8128           vector<const SMDS_MeshNode *> poly_nodes;
8129           vector<int> quantities (nbFaces);
8130
8131           for (int iface = 1; iface <= nbFaces; iface++) {
8132             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8133             quantities[iface - 1] = nbFaceNodes;
8134
8135             for (inode = 1; inode <= nbFaceNodes; inode++) {
8136               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8137
8138               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8139               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8140                 curNode = (*nnIt).second;
8141               }
8142               poly_nodes.push_back(curNode);
8143             }
8144           }
8145           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8146         }
8147       }
8148       else // replace non-polyhedron elements
8149       {
8150         const SMDSAbs_ElementType etyp = elem->GetType();
8151         const int elemId               = elem->GetID();
8152         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8153         uniqueNodes.resize(nbUniqueNodes);
8154
8155         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8156
8157         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8158         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8159         if ( sm && newElem )
8160           sm->AddElement( newElem );
8161         if ( elem != newElem )
8162           ReplaceElemInGroups( elem, newElem, aMesh );
8163       }
8164     }
8165     else {
8166       // Remove invalid regular element or invalid polygon
8167       rmElemIds.push_back( elem->GetID() );
8168     }
8169
8170   } // loop on elements
8171
8172   // Remove bad elements, then equal nodes (order important)
8173
8174   Remove( rmElemIds, false );
8175   Remove( rmNodeIds, true );
8176
8177 }
8178
8179
8180 // ========================================================
8181 // class   : SortableElement
8182 // purpose : allow sorting elements basing on their nodes
8183 // ========================================================
8184 class SortableElement : public set <const SMDS_MeshElement*>
8185 {
8186 public:
8187
8188   SortableElement( const SMDS_MeshElement* theElem )
8189   {
8190     myElem = theElem;
8191     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8192     while ( nodeIt->more() )
8193       this->insert( nodeIt->next() );
8194   }
8195
8196   const SMDS_MeshElement* Get() const
8197   { return myElem; }
8198
8199   void Set(const SMDS_MeshElement* e) const
8200   { myElem = e; }
8201
8202
8203 private:
8204   mutable const SMDS_MeshElement* myElem;
8205 };
8206
8207 //=======================================================================
8208 //function : FindEqualElements
8209 //purpose  : Return list of group of elements built on the same nodes.
8210 //           Search among theElements or in the whole mesh if theElements is empty
8211 //=======================================================================
8212
8213 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8214                                          TListOfListOfElementsID & theGroupsOfElementsID)
8215 {
8216   myLastCreatedElems.Clear();
8217   myLastCreatedNodes.Clear();
8218
8219   typedef map< SortableElement, int > TMapOfNodeSet;
8220   typedef list<int> TGroupOfElems;
8221
8222   if ( theElements.empty() )
8223   { // get all elements in the mesh
8224     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8225     while ( eIt->more() )
8226       theElements.insert( theElements.end(), eIt->next());
8227   }
8228
8229   vector< TGroupOfElems > arrayOfGroups;
8230   TGroupOfElems groupOfElems;
8231   TMapOfNodeSet mapOfNodeSet;
8232
8233   TIDSortedElemSet::iterator elemIt = theElements.begin();
8234   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8235     const SMDS_MeshElement* curElem = *elemIt;
8236     SortableElement SE(curElem);
8237     int ind = -1;
8238     // check uniqueness
8239     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8240     if( !(pp.second) ) {
8241       TMapOfNodeSet::iterator& itSE = pp.first;
8242       ind = (*itSE).second;
8243       arrayOfGroups[ind].push_back(curElem->GetID());
8244     }
8245     else {
8246       groupOfElems.clear();
8247       groupOfElems.push_back(curElem->GetID());
8248       arrayOfGroups.push_back(groupOfElems);
8249       i++;
8250     }
8251   }
8252
8253   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8254   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8255     groupOfElems = *groupIt;
8256     if ( groupOfElems.size() > 1 ) {
8257       groupOfElems.sort();
8258       theGroupsOfElementsID.push_back(groupOfElems);
8259     }
8260   }
8261 }
8262
8263 //=======================================================================
8264 //function : MergeElements
8265 //purpose  : In each given group, substitute all elements by the first one.
8266 //=======================================================================
8267
8268 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8269 {
8270   myLastCreatedElems.Clear();
8271   myLastCreatedNodes.Clear();
8272
8273   typedef list<int> TListOfIDs;
8274   TListOfIDs rmElemIds; // IDs of elems to remove
8275
8276   SMESHDS_Mesh* aMesh = GetMeshDS();
8277
8278   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8279   while ( groupsIt != theGroupsOfElementsID.end() ) {
8280     TListOfIDs& aGroupOfElemID = *groupsIt;
8281     aGroupOfElemID.sort();
8282     int elemIDToKeep = aGroupOfElemID.front();
8283     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8284     aGroupOfElemID.pop_front();
8285     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8286     while ( idIt != aGroupOfElemID.end() ) {
8287       int elemIDToRemove = *idIt;
8288       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8289       // add the kept element in groups of removed one (PAL15188)
8290       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8291       rmElemIds.push_back( elemIDToRemove );
8292       ++idIt;
8293     }
8294     ++groupsIt;
8295   }
8296
8297   Remove( rmElemIds, false );
8298 }
8299
8300 //=======================================================================
8301 //function : MergeEqualElements
8302 //purpose  : Remove all but one of elements built on the same nodes.
8303 //=======================================================================
8304
8305 void SMESH_MeshEditor::MergeEqualElements()
8306 {
8307   TIDSortedElemSet aMeshElements; /* empty input ==
8308                                      to merge equal elements in the whole mesh */
8309   TListOfListOfElementsID aGroupsOfElementsID;
8310   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8311   MergeElements(aGroupsOfElementsID);
8312 }
8313
8314 //=======================================================================
8315 //function : FindFaceInSet
8316 //purpose  : Return a face having linked nodes n1 and n2 and which is
8317 //           - not in avoidSet,
8318 //           - in elemSet provided that !elemSet.empty()
8319 //           i1 and i2 optionally returns indices of n1 and n2
8320 //=======================================================================
8321
8322 const SMDS_MeshElement*
8323 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8324                                 const SMDS_MeshNode*    n2,
8325                                 const TIDSortedElemSet& elemSet,
8326                                 const TIDSortedElemSet& avoidSet,
8327                                 int*                    n1ind,
8328                                 int*                    n2ind)
8329
8330 {
8331   int i1, i2;
8332   const SMDS_MeshElement* face = 0;
8333
8334   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8335   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8336   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8337   {
8338     //MESSAGE("in while ( invElemIt->more() && !face )");
8339     const SMDS_MeshElement* elem = invElemIt->next();
8340     if (avoidSet.count( elem ))
8341       continue;
8342     if ( !elemSet.empty() && !elemSet.count( elem ))
8343       continue;
8344     // index of n1
8345     i1 = elem->GetNodeIndex( n1 );
8346     // find a n2 linked to n1
8347     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8348     for ( int di = -1; di < 2 && !face; di += 2 )
8349     {
8350       i2 = (i1+di+nbN) % nbN;
8351       if ( elem->GetNode( i2 ) == n2 )
8352         face = elem;
8353     }
8354     if ( !face && elem->IsQuadratic())
8355     {
8356       // analysis for quadratic elements using all nodes
8357       const SMDS_VtkFace* F =
8358         dynamic_cast<const SMDS_VtkFace*>(elem);
8359       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8360       // use special nodes iterator
8361       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8362       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8363       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8364       {
8365         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8366         if ( n1 == prevN && n2 == n )
8367         {
8368           face = elem;
8369         }
8370         else if ( n2 == prevN && n1 == n )
8371         {
8372           face = elem; swap( i1, i2 );
8373         }
8374         prevN = n;
8375       }
8376     }
8377   }
8378   if ( n1ind ) *n1ind = i1;
8379   if ( n2ind ) *n2ind = i2;
8380   return face;
8381 }
8382
8383 //=======================================================================
8384 //function : findAdjacentFace
8385 //purpose  :
8386 //=======================================================================
8387
8388 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8389                                                 const SMDS_MeshNode* n2,
8390                                                 const SMDS_MeshElement* elem)
8391 {
8392   TIDSortedElemSet elemSet, avoidSet;
8393   if ( elem )
8394     avoidSet.insert ( elem );
8395   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8396 }
8397
8398 //=======================================================================
8399 //function : FindFreeBorder
8400 //purpose  :
8401 //=======================================================================
8402
8403 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8404
8405 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8406                                        const SMDS_MeshNode*             theSecondNode,
8407                                        const SMDS_MeshNode*             theLastNode,
8408                                        list< const SMDS_MeshNode* > &   theNodes,
8409                                        list< const SMDS_MeshElement* >& theFaces)
8410 {
8411   if ( !theFirstNode || !theSecondNode )
8412     return false;
8413   // find border face between theFirstNode and theSecondNode
8414   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8415   if ( !curElem )
8416     return false;
8417
8418   theFaces.push_back( curElem );
8419   theNodes.push_back( theFirstNode );
8420   theNodes.push_back( theSecondNode );
8421
8422   //vector<const SMDS_MeshNode*> nodes;
8423   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8424   TIDSortedElemSet foundElems;
8425   bool needTheLast = ( theLastNode != 0 );
8426
8427   while ( nStart != theLastNode ) {
8428     if ( nStart == theFirstNode )
8429       return !needTheLast;
8430
8431     // find all free border faces sharing form nStart
8432
8433     list< const SMDS_MeshElement* > curElemList;
8434     list< const SMDS_MeshNode* > nStartList;
8435     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8436     while ( invElemIt->more() ) {
8437       const SMDS_MeshElement* e = invElemIt->next();
8438       if ( e == curElem || foundElems.insert( e ).second ) {
8439         // get nodes
8440         int iNode = 0, nbNodes = e->NbNodes();
8441         //const SMDS_MeshNode* nodes[nbNodes+1];
8442         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8443
8444         if(e->IsQuadratic()) {
8445           const SMDS_VtkFace* F =
8446             dynamic_cast<const SMDS_VtkFace*>(e);
8447           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8448           // use special nodes iterator
8449           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8450           while( anIter->more() ) {
8451             nodes[ iNode++ ] = cast2Node(anIter->next());
8452           }
8453         }
8454         else {
8455           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8456           while ( nIt->more() )
8457             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8458         }
8459         nodes[ iNode ] = nodes[ 0 ];
8460         // check 2 links
8461         for ( iNode = 0; iNode < nbNodes; iNode++ )
8462           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8463                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8464               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8465           {
8466             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8467             curElemList.push_back( e );
8468           }
8469       }
8470     }
8471     // analyse the found
8472
8473     int nbNewBorders = curElemList.size();
8474     if ( nbNewBorders == 0 ) {
8475       // no free border furthermore
8476       return !needTheLast;
8477     }
8478     else if ( nbNewBorders == 1 ) {
8479       // one more element found
8480       nIgnore = nStart;
8481       nStart = nStartList.front();
8482       curElem = curElemList.front();
8483       theFaces.push_back( curElem );
8484       theNodes.push_back( nStart );
8485     }
8486     else {
8487       // several continuations found
8488       list< const SMDS_MeshElement* >::iterator curElemIt;
8489       list< const SMDS_MeshNode* >::iterator nStartIt;
8490       // check if one of them reached the last node
8491       if ( needTheLast ) {
8492         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8493              curElemIt!= curElemList.end();
8494              curElemIt++, nStartIt++ )
8495           if ( *nStartIt == theLastNode ) {
8496             theFaces.push_back( *curElemIt );
8497             theNodes.push_back( *nStartIt );
8498             return true;
8499           }
8500       }
8501       // find the best free border by the continuations
8502       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8503       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8504       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8505            curElemIt!= curElemList.end();
8506            curElemIt++, nStartIt++ )
8507       {
8508         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8509         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8510         // find one more free border
8511         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8512           cNL->clear();
8513           cFL->clear();
8514         }
8515         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8516           // choice: clear a worse one
8517           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8518           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8519           contNodes[ iWorse ].clear();
8520           contFaces[ iWorse ].clear();
8521         }
8522       }
8523       if ( contNodes[0].empty() && contNodes[1].empty() )
8524         return false;
8525
8526       // append the best free border
8527       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8528       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8529       theNodes.pop_back(); // remove nIgnore
8530       theNodes.pop_back(); // remove nStart
8531       theFaces.pop_back(); // remove curElem
8532       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8533       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8534       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8535       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8536       return true;
8537
8538     } // several continuations found
8539   } // while ( nStart != theLastNode )
8540
8541   return true;
8542 }
8543
8544 //=======================================================================
8545 //function : CheckFreeBorderNodes
8546 //purpose  : Return true if the tree nodes are on a free border
8547 //=======================================================================
8548
8549 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8550                                             const SMDS_MeshNode* theNode2,
8551                                             const SMDS_MeshNode* theNode3)
8552 {
8553   list< const SMDS_MeshNode* > nodes;
8554   list< const SMDS_MeshElement* > faces;
8555   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8556 }
8557
8558 //=======================================================================
8559 //function : SewFreeBorder
8560 //purpose  :
8561 //=======================================================================
8562
8563 SMESH_MeshEditor::Sew_Error
8564 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8565                                  const SMDS_MeshNode* theBordSecondNode,
8566                                  const SMDS_MeshNode* theBordLastNode,
8567                                  const SMDS_MeshNode* theSideFirstNode,
8568                                  const SMDS_MeshNode* theSideSecondNode,
8569                                  const SMDS_MeshNode* theSideThirdNode,
8570                                  const bool           theSideIsFreeBorder,
8571                                  const bool           toCreatePolygons,
8572                                  const bool           toCreatePolyedrs)
8573 {
8574   myLastCreatedElems.Clear();
8575   myLastCreatedNodes.Clear();
8576
8577   MESSAGE("::SewFreeBorder()");
8578   Sew_Error aResult = SEW_OK;
8579
8580   // ====================================
8581   //    find side nodes and elements
8582   // ====================================
8583
8584   list< const SMDS_MeshNode* > nSide[ 2 ];
8585   list< const SMDS_MeshElement* > eSide[ 2 ];
8586   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8587   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8588
8589   // Free border 1
8590   // --------------
8591   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8592                       nSide[0], eSide[0])) {
8593     MESSAGE(" Free Border 1 not found " );
8594     aResult = SEW_BORDER1_NOT_FOUND;
8595   }
8596   if (theSideIsFreeBorder) {
8597     // Free border 2
8598     // --------------
8599     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8600                         nSide[1], eSide[1])) {
8601       MESSAGE(" Free Border 2 not found " );
8602       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8603     }
8604   }
8605   if ( aResult != SEW_OK )
8606     return aResult;
8607
8608   if (!theSideIsFreeBorder) {
8609     // Side 2
8610     // --------------
8611
8612     // -------------------------------------------------------------------------
8613     // Algo:
8614     // 1. If nodes to merge are not coincident, move nodes of the free border
8615     //    from the coord sys defined by the direction from the first to last
8616     //    nodes of the border to the correspondent sys of the side 2
8617     // 2. On the side 2, find the links most co-directed with the correspondent
8618     //    links of the free border
8619     // -------------------------------------------------------------------------
8620
8621     // 1. Since sewing may break if there are volumes to split on the side 2,
8622     //    we wont move nodes but just compute new coordinates for them
8623     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8624     TNodeXYZMap nBordXYZ;
8625     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8626     list< const SMDS_MeshNode* >::iterator nBordIt;
8627
8628     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8629     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8630     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8631     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8632     double tol2 = 1.e-8;
8633     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8634     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8635       // Need node movement.
8636
8637       // find X and Z axes to create trsf
8638       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8639       gp_Vec X = Zs ^ Zb;
8640       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8641         // Zb || Zs
8642         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8643
8644       // coord systems
8645       gp_Ax3 toBordAx( Pb1, Zb, X );
8646       gp_Ax3 fromSideAx( Ps1, Zs, X );
8647       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8648       // set trsf
8649       gp_Trsf toBordSys, fromSide2Sys;
8650       toBordSys.SetTransformation( toBordAx );
8651       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8652       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8653
8654       // move
8655       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8656         const SMDS_MeshNode* n = *nBordIt;
8657         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8658         toBordSys.Transforms( xyz );
8659         fromSide2Sys.Transforms( xyz );
8660         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8661       }
8662     }
8663     else {
8664       // just insert nodes XYZ in the nBordXYZ map
8665       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8666         const SMDS_MeshNode* n = *nBordIt;
8667         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8668       }
8669     }
8670
8671     // 2. On the side 2, find the links most co-directed with the correspondent
8672     //    links of the free border
8673
8674     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8675     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8676     sideNodes.push_back( theSideFirstNode );
8677
8678     bool hasVolumes = false;
8679     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8680     set<long> foundSideLinkIDs, checkedLinkIDs;
8681     SMDS_VolumeTool volume;
8682     //const SMDS_MeshNode* faceNodes[ 4 ];
8683
8684     const SMDS_MeshNode*    sideNode;
8685     const SMDS_MeshElement* sideElem;
8686     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8687     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8688     nBordIt = bordNodes.begin();
8689     nBordIt++;
8690     // border node position and border link direction to compare with
8691     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8692     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8693     // choose next side node by link direction or by closeness to
8694     // the current border node:
8695     bool searchByDir = ( *nBordIt != theBordLastNode );
8696     do {
8697       // find the next node on the Side 2
8698       sideNode = 0;
8699       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8700       long linkID;
8701       checkedLinkIDs.clear();
8702       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8703
8704       // loop on inverse elements of current node (prevSideNode) on the Side 2
8705       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8706       while ( invElemIt->more() )
8707       {
8708         const SMDS_MeshElement* elem = invElemIt->next();
8709         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8710         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8711         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8712         bool isVolume = volume.Set( elem );
8713         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8714         if ( isVolume ) // --volume
8715           hasVolumes = true;
8716         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8717           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8718           if(elem->IsQuadratic()) {
8719             const SMDS_VtkFace* F =
8720               dynamic_cast<const SMDS_VtkFace*>(elem);
8721             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8722             // use special nodes iterator
8723             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8724             while( anIter->more() ) {
8725               nodes[ iNode ] = cast2Node(anIter->next());
8726               if ( nodes[ iNode++ ] == prevSideNode )
8727                 iPrevNode = iNode - 1;
8728             }
8729           }
8730           else {
8731             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8732             while ( nIt->more() ) {
8733               nodes[ iNode ] = cast2Node( nIt->next() );
8734               if ( nodes[ iNode++ ] == prevSideNode )
8735                 iPrevNode = iNode - 1;
8736             }
8737           }
8738           // there are 2 links to check
8739           nbNodes = 2;
8740         }
8741         else // --edge
8742           continue;
8743         // loop on links, to be precise, on the second node of links
8744         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8745           const SMDS_MeshNode* n = nodes[ iNode ];
8746           if ( isVolume ) {
8747             if ( !volume.IsLinked( n, prevSideNode ))
8748               continue;
8749           }
8750           else {
8751             if ( iNode ) // a node before prevSideNode
8752               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8753             else         // a node after prevSideNode
8754               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8755           }
8756           // check if this link was already used
8757           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8758           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8759           if (!isJustChecked &&
8760               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8761           {
8762             // test a link geometrically
8763             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8764             bool linkIsBetter = false;
8765             double dot = 0.0, dist = 0.0;
8766             if ( searchByDir ) { // choose most co-directed link
8767               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8768               linkIsBetter = ( dot > maxDot );
8769             }
8770             else { // choose link with the node closest to bordPos
8771               dist = ( nextXYZ - bordPos ).SquareModulus();
8772               linkIsBetter = ( dist < minDist );
8773             }
8774             if ( linkIsBetter ) {
8775               maxDot = dot;
8776               minDist = dist;
8777               linkID = iLink;
8778               sideNode = n;
8779               sideElem = elem;
8780             }
8781           }
8782         }
8783       } // loop on inverse elements of prevSideNode
8784
8785       if ( !sideNode ) {
8786         MESSAGE(" Cant find path by links of the Side 2 ");
8787         return SEW_BAD_SIDE_NODES;
8788       }
8789       sideNodes.push_back( sideNode );
8790       sideElems.push_back( sideElem );
8791       foundSideLinkIDs.insert ( linkID );
8792       prevSideNode = sideNode;
8793
8794       if ( *nBordIt == theBordLastNode )
8795         searchByDir = false;
8796       else {
8797         // find the next border link to compare with
8798         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8799         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8800         // move to next border node if sideNode is before forward border node (bordPos)
8801         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8802           prevBordNode = *nBordIt;
8803           nBordIt++;
8804           bordPos = nBordXYZ[ *nBordIt ];
8805           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8806           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8807         }
8808       }
8809     }
8810     while ( sideNode != theSideSecondNode );
8811
8812     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8813       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8814       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8815     }
8816   } // end nodes search on the side 2
8817
8818   // ============================
8819   // sew the border to the side 2
8820   // ============================
8821
8822   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8823   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8824
8825   TListOfListOfNodes nodeGroupsToMerge;
8826   if ( nbNodes[0] == nbNodes[1] ||
8827        ( theSideIsFreeBorder && !theSideThirdNode)) {
8828
8829     // all nodes are to be merged
8830
8831     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8832          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8833          nIt[0]++, nIt[1]++ )
8834     {
8835       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8836       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8837       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8838     }
8839   }
8840   else {
8841
8842     // insert new nodes into the border and the side to get equal nb of segments
8843
8844     // get normalized parameters of nodes on the borders
8845     //double param[ 2 ][ maxNbNodes ];
8846     double* param[ 2 ];
8847     param[0] = new double [ maxNbNodes ];
8848     param[1] = new double [ maxNbNodes ];
8849     int iNode, iBord;
8850     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8851       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8852       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8853       const SMDS_MeshNode* nPrev = *nIt;
8854       double bordLength = 0;
8855       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8856         const SMDS_MeshNode* nCur = *nIt;
8857         gp_XYZ segment (nCur->X() - nPrev->X(),
8858                         nCur->Y() - nPrev->Y(),
8859                         nCur->Z() - nPrev->Z());
8860         double segmentLen = segment.Modulus();
8861         bordLength += segmentLen;
8862         param[ iBord ][ iNode ] = bordLength;
8863         nPrev = nCur;
8864       }
8865       // normalize within [0,1]
8866       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8867         param[ iBord ][ iNode ] /= bordLength;
8868       }
8869     }
8870
8871     // loop on border segments
8872     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8873     int i[ 2 ] = { 0, 0 };
8874     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8875     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8876
8877     TElemOfNodeListMap insertMap;
8878     TElemOfNodeListMap::iterator insertMapIt;
8879     // insertMap is
8880     // key:   elem to insert nodes into
8881     // value: 2 nodes to insert between + nodes to be inserted
8882     do {
8883       bool next[ 2 ] = { false, false };
8884
8885       // find min adjacent segment length after sewing
8886       double nextParam = 10., prevParam = 0;
8887       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8888         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8889           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8890         if ( i[ iBord ] > 0 )
8891           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8892       }
8893       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8894       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8895       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8896
8897       // choose to insert or to merge nodes
8898       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8899       if ( Abs( du ) <= minSegLen * 0.2 ) {
8900         // merge
8901         // ------
8902         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8903         const SMDS_MeshNode* n0 = *nIt[0];
8904         const SMDS_MeshNode* n1 = *nIt[1];
8905         nodeGroupsToMerge.back().push_back( n1 );
8906         nodeGroupsToMerge.back().push_back( n0 );
8907         // position of node of the border changes due to merge
8908         param[ 0 ][ i[0] ] += du;
8909         // move n1 for the sake of elem shape evaluation during insertion.
8910         // n1 will be removed by MergeNodes() anyway
8911         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8912         next[0] = next[1] = true;
8913       }
8914       else {
8915         // insert
8916         // ------
8917         int intoBord = ( du < 0 ) ? 0 : 1;
8918         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8919         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8920         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8921         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8922         if ( intoBord == 1 ) {
8923           // move node of the border to be on a link of elem of the side
8924           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8925           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8926           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8927           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8928           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8929         }
8930         insertMapIt = insertMap.find( elem );
8931         bool notFound = ( insertMapIt == insertMap.end() );
8932         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8933         if ( otherLink ) {
8934           // insert into another link of the same element:
8935           // 1. perform insertion into the other link of the elem
8936           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8937           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8938           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8939           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8940           // 2. perform insertion into the link of adjacent faces
8941           while (true) {
8942             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8943             if ( adjElem )
8944               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8945             else
8946               break;
8947           }
8948           if (toCreatePolyedrs) {
8949             // perform insertion into the links of adjacent volumes
8950             UpdateVolumes(n12, n22, nodeList);
8951           }
8952           // 3. find an element appeared on n1 and n2 after the insertion
8953           insertMap.erase( elem );
8954           elem = findAdjacentFace( n1, n2, 0 );
8955         }
8956         if ( notFound || otherLink ) {
8957           // add element and nodes of the side into the insertMap
8958           insertMapIt = insertMap.insert
8959             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8960           (*insertMapIt).second.push_back( n1 );
8961           (*insertMapIt).second.push_back( n2 );
8962         }
8963         // add node to be inserted into elem
8964         (*insertMapIt).second.push_back( nIns );
8965         next[ 1 - intoBord ] = true;
8966       }
8967
8968       // go to the next segment
8969       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8970         if ( next[ iBord ] ) {
8971           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8972             eIt[ iBord ]++;
8973           nPrev[ iBord ] = *nIt[ iBord ];
8974           nIt[ iBord ]++; i[ iBord ]++;
8975         }
8976       }
8977     }
8978     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8979
8980     // perform insertion of nodes into elements
8981
8982     for (insertMapIt = insertMap.begin();
8983          insertMapIt != insertMap.end();
8984          insertMapIt++ )
8985     {
8986       const SMDS_MeshElement* elem = (*insertMapIt).first;
8987       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8988       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8989       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8990
8991       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8992
8993       if ( !theSideIsFreeBorder ) {
8994         // look for and insert nodes into the faces adjacent to elem
8995         while (true) {
8996           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8997           if ( adjElem )
8998             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8999           else
9000             break;
9001         }
9002       }
9003       if (toCreatePolyedrs) {
9004         // perform insertion into the links of adjacent volumes
9005         UpdateVolumes(n1, n2, nodeList);
9006       }
9007     }
9008
9009     delete param[0];
9010     delete param[1];
9011   } // end: insert new nodes
9012
9013   MergeNodes ( nodeGroupsToMerge );
9014
9015   return aResult;
9016 }
9017
9018 //=======================================================================
9019 //function : InsertNodesIntoLink
9020 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9021 //           and theBetweenNode2 and split theElement
9022 //=======================================================================
9023
9024 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9025                                            const SMDS_MeshNode*        theBetweenNode1,
9026                                            const SMDS_MeshNode*        theBetweenNode2,
9027                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9028                                            const bool                  toCreatePoly)
9029 {
9030   if ( theFace->GetType() != SMDSAbs_Face ) return;
9031
9032   // find indices of 2 link nodes and of the rest nodes
9033   int iNode = 0, il1, il2, i3, i4;
9034   il1 = il2 = i3 = i4 = -1;
9035   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9036   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9037
9038   if(theFace->IsQuadratic()) {
9039     const SMDS_VtkFace* F =
9040       dynamic_cast<const SMDS_VtkFace*>(theFace);
9041     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9042     // use special nodes iterator
9043     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9044     while( anIter->more() ) {
9045       const SMDS_MeshNode* n = cast2Node(anIter->next());
9046       if ( n == theBetweenNode1 )
9047         il1 = iNode;
9048       else if ( n == theBetweenNode2 )
9049         il2 = iNode;
9050       else if ( i3 < 0 )
9051         i3 = iNode;
9052       else
9053         i4 = iNode;
9054       nodes[ iNode++ ] = n;
9055     }
9056   }
9057   else {
9058     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9059     while ( nodeIt->more() ) {
9060       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9061       if ( n == theBetweenNode1 )
9062         il1 = iNode;
9063       else if ( n == theBetweenNode2 )
9064         il2 = iNode;
9065       else if ( i3 < 0 )
9066         i3 = iNode;
9067       else
9068         i4 = iNode;
9069       nodes[ iNode++ ] = n;
9070     }
9071   }
9072   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9073     return ;
9074
9075   // arrange link nodes to go one after another regarding the face orientation
9076   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9077   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9078   if ( reverse ) {
9079     iNode = il1;
9080     il1 = il2;
9081     il2 = iNode;
9082     aNodesToInsert.reverse();
9083   }
9084   // check that not link nodes of a quadrangles are in good order
9085   int nbFaceNodes = theFace->NbNodes();
9086   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9087     iNode = i3;
9088     i3 = i4;
9089     i4 = iNode;
9090   }
9091
9092   if (toCreatePoly || theFace->IsPoly()) {
9093
9094     iNode = 0;
9095     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9096
9097     // add nodes of face up to first node of link
9098     bool isFLN = false;
9099
9100     if(theFace->IsQuadratic()) {
9101       const SMDS_VtkFace* F =
9102         dynamic_cast<const SMDS_VtkFace*>(theFace);
9103       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9104       // use special nodes iterator
9105       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9106       while( anIter->more()  && !isFLN ) {
9107         const SMDS_MeshNode* n = cast2Node(anIter->next());
9108         poly_nodes[iNode++] = n;
9109         if (n == nodes[il1]) {
9110           isFLN = true;
9111         }
9112       }
9113       // add nodes to insert
9114       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9115       for (; nIt != aNodesToInsert.end(); nIt++) {
9116         poly_nodes[iNode++] = *nIt;
9117       }
9118       // add nodes of face starting from last node of link
9119       while ( anIter->more() ) {
9120         poly_nodes[iNode++] = cast2Node(anIter->next());
9121       }
9122     }
9123     else {
9124       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9125       while ( nodeIt->more() && !isFLN ) {
9126         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9127         poly_nodes[iNode++] = n;
9128         if (n == nodes[il1]) {
9129           isFLN = true;
9130         }
9131       }
9132       // add nodes to insert
9133       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9134       for (; nIt != aNodesToInsert.end(); nIt++) {
9135         poly_nodes[iNode++] = *nIt;
9136       }
9137       // add nodes of face starting from last node of link
9138       while ( nodeIt->more() ) {
9139         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9140         poly_nodes[iNode++] = n;
9141       }
9142     }
9143
9144     // edit or replace the face
9145     SMESHDS_Mesh *aMesh = GetMeshDS();
9146
9147     if (theFace->IsPoly()) {
9148       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9149     }
9150     else {
9151       int aShapeId = FindShape( theFace );
9152
9153       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9154       myLastCreatedElems.Append(newElem);
9155       if ( aShapeId && newElem )
9156         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9157
9158       aMesh->RemoveElement(theFace);
9159     }
9160     return;
9161   }
9162
9163   SMESHDS_Mesh *aMesh = GetMeshDS();
9164   if( !theFace->IsQuadratic() ) {
9165
9166     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9167     int nbLinkNodes = 2 + aNodesToInsert.size();
9168     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9169     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9170     linkNodes[ 0 ] = nodes[ il1 ];
9171     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9172     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9173     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9174       linkNodes[ iNode++ ] = *nIt;
9175     }
9176     // decide how to split a quadrangle: compare possible variants
9177     // and choose which of splits to be a quadrangle
9178     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9179     if ( nbFaceNodes == 3 ) {
9180       iBestQuad = nbSplits;
9181       i4 = i3;
9182     }
9183     else if ( nbFaceNodes == 4 ) {
9184       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9185       double aBestRate = DBL_MAX;
9186       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9187         i1 = 0; i2 = 1;
9188         double aBadRate = 0;
9189         // evaluate elements quality
9190         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9191           if ( iSplit == iQuad ) {
9192             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9193                                    linkNodes[ i2++ ],
9194                                    nodes[ i3 ],
9195                                    nodes[ i4 ]);
9196             aBadRate += getBadRate( &quad, aCrit );
9197           }
9198           else {
9199             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9200                                    linkNodes[ i2++ ],
9201                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9202             aBadRate += getBadRate( &tria, aCrit );
9203           }
9204         }
9205         // choice
9206         if ( aBadRate < aBestRate ) {
9207           iBestQuad = iQuad;
9208           aBestRate = aBadRate;
9209         }
9210       }
9211     }
9212
9213     // create new elements
9214     int aShapeId = FindShape( theFace );
9215
9216     i1 = 0; i2 = 1;
9217     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9218       SMDS_MeshElement* newElem = 0;
9219       if ( iSplit == iBestQuad )
9220         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9221                                   linkNodes[ i2++ ],
9222                                   nodes[ i3 ],
9223                                   nodes[ i4 ]);
9224       else
9225         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9226                                   linkNodes[ i2++ ],
9227                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9228       myLastCreatedElems.Append(newElem);
9229       if ( aShapeId && newElem )
9230         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9231     }
9232
9233     // change nodes of theFace
9234     const SMDS_MeshNode* newNodes[ 4 ];
9235     newNodes[ 0 ] = linkNodes[ i1 ];
9236     newNodes[ 1 ] = linkNodes[ i2 ];
9237     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9238     newNodes[ 3 ] = nodes[ i4 ];
9239     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9240     const SMDS_MeshElement* newElem = 0;
9241     if (iSplit == iBestQuad)
9242       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9243     else
9244       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9245     myLastCreatedElems.Append(newElem);
9246     if ( aShapeId && newElem )
9247       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9248 } // end if(!theFace->IsQuadratic())
9249   else { // theFace is quadratic
9250     // we have to split theFace on simple triangles and one simple quadrangle
9251     int tmp = il1/2;
9252     int nbshift = tmp*2;
9253     // shift nodes in nodes[] by nbshift
9254     int i,j;
9255     for(i=0; i<nbshift; i++) {
9256       const SMDS_MeshNode* n = nodes[0];
9257       for(j=0; j<nbFaceNodes-1; j++) {
9258         nodes[j] = nodes[j+1];
9259       }
9260       nodes[nbFaceNodes-1] = n;
9261     }
9262     il1 = il1 - nbshift;
9263     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9264     //   n0      n1     n2    n0      n1     n2
9265     //     +-----+-----+        +-----+-----+
9266     //      \         /         |           |
9267     //       \       /          |           |
9268     //      n5+     +n3       n7+           +n3
9269     //         \   /            |           |
9270     //          \ /             |           |
9271     //           +              +-----+-----+
9272     //           n4           n6      n5     n4
9273
9274     // create new elements
9275     int aShapeId = FindShape( theFace );
9276
9277     int n1,n2,n3;
9278     if(nbFaceNodes==6) { // quadratic triangle
9279       SMDS_MeshElement* newElem =
9280         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9281       myLastCreatedElems.Append(newElem);
9282       if ( aShapeId && newElem )
9283         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9284       if(theFace->IsMediumNode(nodes[il1])) {
9285         // create quadrangle
9286         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9287         myLastCreatedElems.Append(newElem);
9288         if ( aShapeId && newElem )
9289           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9290         n1 = 1;
9291         n2 = 2;
9292         n3 = 3;
9293       }
9294       else {
9295         // create quadrangle
9296         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9297         myLastCreatedElems.Append(newElem);
9298         if ( aShapeId && newElem )
9299           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9300         n1 = 0;
9301         n2 = 1;
9302         n3 = 5;
9303       }
9304     }
9305     else { // nbFaceNodes==8 - quadratic quadrangle
9306       SMDS_MeshElement* newElem =
9307         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9308       myLastCreatedElems.Append(newElem);
9309       if ( aShapeId && newElem )
9310         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9311       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9312       myLastCreatedElems.Append(newElem);
9313       if ( aShapeId && newElem )
9314         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9315       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9316       myLastCreatedElems.Append(newElem);
9317       if ( aShapeId && newElem )
9318         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9319       if(theFace->IsMediumNode(nodes[il1])) {
9320         // create quadrangle
9321         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9322         myLastCreatedElems.Append(newElem);
9323         if ( aShapeId && newElem )
9324           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9325         n1 = 1;
9326         n2 = 2;
9327         n3 = 3;
9328       }
9329       else {
9330         // create quadrangle
9331         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9332         myLastCreatedElems.Append(newElem);
9333         if ( aShapeId && newElem )
9334           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9335         n1 = 0;
9336         n2 = 1;
9337         n3 = 7;
9338       }
9339     }
9340     // create needed triangles using n1,n2,n3 and inserted nodes
9341     int nbn = 2 + aNodesToInsert.size();
9342     //const SMDS_MeshNode* aNodes[nbn];
9343     vector<const SMDS_MeshNode*> aNodes(nbn);
9344     aNodes[0] = nodes[n1];
9345     aNodes[nbn-1] = nodes[n2];
9346     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9347     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9348       aNodes[iNode++] = *nIt;
9349     }
9350     for(i=1; i<nbn; i++) {
9351       SMDS_MeshElement* newElem =
9352         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9353       myLastCreatedElems.Append(newElem);
9354       if ( aShapeId && newElem )
9355         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9356     }
9357   }
9358   // remove old face
9359   aMesh->RemoveElement(theFace);
9360 }
9361
9362 //=======================================================================
9363 //function : UpdateVolumes
9364 //purpose  :
9365 //=======================================================================
9366 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9367                                       const SMDS_MeshNode*        theBetweenNode2,
9368                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9369 {
9370   myLastCreatedElems.Clear();
9371   myLastCreatedNodes.Clear();
9372
9373   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9374   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9375     const SMDS_MeshElement* elem = invElemIt->next();
9376
9377     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9378     SMDS_VolumeTool aVolume (elem);
9379     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9380       continue;
9381
9382     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9383     int iface, nbFaces = aVolume.NbFaces();
9384     vector<const SMDS_MeshNode *> poly_nodes;
9385     vector<int> quantities (nbFaces);
9386
9387     for (iface = 0; iface < nbFaces; iface++) {
9388       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9389       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9390       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9391
9392       for (int inode = 0; inode < nbFaceNodes; inode++) {
9393         poly_nodes.push_back(faceNodes[inode]);
9394
9395         if (nbInserted == 0) {
9396           if (faceNodes[inode] == theBetweenNode1) {
9397             if (faceNodes[inode + 1] == theBetweenNode2) {
9398               nbInserted = theNodesToInsert.size();
9399
9400               // add nodes to insert
9401               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9402               for (; nIt != theNodesToInsert.end(); nIt++) {
9403                 poly_nodes.push_back(*nIt);
9404               }
9405             }
9406           }
9407           else if (faceNodes[inode] == theBetweenNode2) {
9408             if (faceNodes[inode + 1] == theBetweenNode1) {
9409               nbInserted = theNodesToInsert.size();
9410
9411               // add nodes to insert in reversed order
9412               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9413               nIt--;
9414               for (; nIt != theNodesToInsert.begin(); nIt--) {
9415                 poly_nodes.push_back(*nIt);
9416               }
9417               poly_nodes.push_back(*nIt);
9418             }
9419           }
9420           else {
9421           }
9422         }
9423       }
9424       quantities[iface] = nbFaceNodes + nbInserted;
9425     }
9426
9427     // Replace or update the volume
9428     SMESHDS_Mesh *aMesh = GetMeshDS();
9429
9430     if (elem->IsPoly()) {
9431       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9432
9433     }
9434     else {
9435       int aShapeId = FindShape( elem );
9436
9437       SMDS_MeshElement* newElem =
9438         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9439       myLastCreatedElems.Append(newElem);
9440       if (aShapeId && newElem)
9441         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9442
9443       aMesh->RemoveElement(elem);
9444     }
9445   }
9446 }
9447
9448 namespace
9449 {
9450   //================================================================================
9451   /*!
9452    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9453    */
9454   //================================================================================
9455
9456   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9457                            vector<const SMDS_MeshNode *> & nodes,
9458                            vector<int> &                   nbNodeInFaces )
9459   {
9460     nodes.clear();
9461     nbNodeInFaces.clear();
9462     SMDS_VolumeTool vTool ( elem );
9463     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9464     {
9465       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9466       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9467       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9468     }
9469   }
9470 }
9471
9472 //=======================================================================
9473 /*!
9474  * \brief Convert elements contained in a submesh to quadratic
9475  * \return int - nb of checked elements
9476  */
9477 //=======================================================================
9478
9479 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9480                                              SMESH_MesherHelper& theHelper,
9481                                              const bool          theForce3d)
9482 {
9483   int nbElem = 0;
9484   if( !theSm ) return nbElem;
9485
9486   vector<int> nbNodeInFaces;
9487   vector<const SMDS_MeshNode *> nodes;
9488   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9489   while(ElemItr->more())
9490   {
9491     nbElem++;
9492     const SMDS_MeshElement* elem = ElemItr->next();
9493     if( !elem || elem->IsQuadratic() ) continue;
9494
9495     // get elem data needed to re-create it
9496     //
9497     const int id                        = elem->GetID();
9498     const int nbNodes                   = elem->NbNodes();
9499     const SMDSAbs_ElementType aType     = elem->GetType();
9500     const SMDSAbs_EntityType  aGeomType = elem->GetEntityType();
9501     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9502     if ( aGeomType == SMDSEntity_Polyhedra )
9503       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9504     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9505       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9506
9507     // remove a linear element
9508     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9509
9510     const SMDS_MeshElement* NewElem = 0;
9511
9512     switch( aType )
9513     {
9514     case SMDSAbs_Edge :
9515       {
9516         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9517         break;
9518       }
9519     case SMDSAbs_Face :
9520       {
9521         switch(nbNodes)
9522         {
9523         case 3:
9524           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9525           break;
9526         case 4:
9527           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9528           break;
9529         default:
9530           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9531           continue;
9532         }
9533         break;
9534       }
9535     case SMDSAbs_Volume :
9536       {
9537         switch( aGeomType )
9538         {
9539         case SMDSEntity_Tetra:
9540           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9541           break;
9542         case SMDSEntity_Pyramid:
9543           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9544           break;
9545         case SMDSEntity_Penta:
9546           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9547           break;
9548         case SMDSEntity_Hexa:
9549           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9550                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9551           break;
9552         case SMDSEntity_Hexagonal_Prism:
9553         default:
9554           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9555         }
9556         break;
9557       }
9558     default :
9559       continue;
9560     }
9561     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9562     if( NewElem )
9563       theSm->AddElement( NewElem );
9564   }
9565   return nbElem;
9566 }
9567
9568 //=======================================================================
9569 //function : ConvertToQuadratic
9570 //purpose  :
9571 //=======================================================================
9572
9573 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9574 {
9575   SMESHDS_Mesh* meshDS = GetMeshDS();
9576
9577   SMESH_MesherHelper aHelper(*myMesh);
9578   aHelper.SetIsQuadratic( true );
9579
9580   int nbCheckedElems = 0;
9581   if ( myMesh->HasShapeToMesh() )
9582   {
9583     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9584     {
9585       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9586       while ( smIt->more() ) {
9587         SMESH_subMesh* sm = smIt->next();
9588         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9589           aHelper.SetSubShape( sm->GetSubShape() );
9590           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9591         }
9592       }
9593     }
9594   }
9595   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9596   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9597   {
9598     SMESHDS_SubMesh *smDS = 0;
9599     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9600     while(aEdgeItr->more())
9601     {
9602       const SMDS_MeshEdge* edge = aEdgeItr->next();
9603       if(edge && !edge->IsQuadratic())
9604       {
9605         int id = edge->GetID();
9606         //MESSAGE("edge->GetID() " << id);
9607         const SMDS_MeshNode* n1 = edge->GetNode(0);
9608         const SMDS_MeshNode* n2 = edge->GetNode(1);
9609
9610         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9611
9612         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9613         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9614       }
9615     }
9616     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9617     while(aFaceItr->more())
9618     {
9619       const SMDS_MeshFace* face = aFaceItr->next();
9620       if(!face || face->IsQuadratic() ) continue;
9621
9622       const int id = face->GetID();
9623       const SMDSAbs_EntityType type = face->GetEntityType();
9624       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9625
9626       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9627
9628       SMDS_MeshFace * NewFace = 0;
9629       switch( type )
9630       {
9631       case SMDSEntity_Triangle:
9632         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9633         break;
9634       case SMDSEntity_Quadrangle:
9635         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9636         break;
9637       default:
9638         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9639       }
9640       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9641     }
9642     vector<int> nbNodeInFaces;
9643     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9644     while(aVolumeItr->more())
9645     {
9646       const SMDS_MeshVolume* volume = aVolumeItr->next();
9647       if(!volume || volume->IsQuadratic() ) continue;
9648
9649       const int id = volume->GetID();
9650       const SMDSAbs_EntityType type = volume->GetEntityType();
9651       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9652       if ( type == SMDSEntity_Polyhedra )
9653         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9654       else if ( type == SMDSEntity_Hexagonal_Prism )
9655         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9656
9657       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9658
9659       SMDS_MeshVolume * NewVolume = 0;
9660       switch ( type )
9661       {
9662       case SMDSEntity_Tetra:
9663         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9664         break;
9665       case SMDSEntity_Hexa:
9666         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9667                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9668         break;
9669       case SMDSEntity_Pyramid:
9670         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9671                                       nodes[3], nodes[4], id, theForce3d);
9672         break;
9673       case SMDSEntity_Penta:
9674         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9675                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9676         break;
9677       case SMDSEntity_Hexagonal_Prism:
9678       default:
9679         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9680       }
9681       ReplaceElemInGroups(volume, NewVolume, meshDS);
9682     }
9683   }
9684
9685   if ( !theForce3d )
9686   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9687     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9688     aHelper.FixQuadraticElements(myError);
9689   }
9690 }
9691
9692 //================================================================================
9693 /*!
9694  * \brief Makes given elements quadratic
9695  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9696  *  \param theElements - elements to make quadratic
9697  */
9698 //================================================================================
9699
9700 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9701                                           TIDSortedElemSet& theElements)
9702 {
9703   if ( theElements.empty() ) return;
9704
9705   // we believe that all theElements are of the same type
9706   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9707
9708   // get all nodes shared by theElements
9709   TIDSortedNodeSet allNodes;
9710   TIDSortedElemSet::iterator eIt = theElements.begin();
9711   for ( ; eIt != theElements.end(); ++eIt )
9712     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9713
9714   // complete theElements with elements of lower dim whose all nodes are in allNodes
9715
9716   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9717   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9718   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9719   for ( ; nIt != allNodes.end(); ++nIt )
9720   {
9721     const SMDS_MeshNode* n = *nIt;
9722     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9723     while ( invIt->more() )
9724     {
9725       const SMDS_MeshElement* e = invIt->next();
9726       if ( e->IsQuadratic() )
9727       {
9728         quadAdjacentElems[ e->GetType() ].insert( e );
9729         continue;
9730       }
9731       if ( e->GetType() >= elemType )
9732       {
9733         continue; // same type of more complex linear element
9734       }
9735
9736       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9737         continue; // e is already checked
9738
9739       // check nodes
9740       bool allIn = true;
9741       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9742       while ( nodeIt->more() && allIn )
9743         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9744       if ( allIn )
9745         theElements.insert(e );
9746     }
9747   }
9748
9749   SMESH_MesherHelper helper(*myMesh);
9750   helper.SetIsQuadratic( true );
9751
9752   // add links of quadratic adjacent elements to the helper
9753
9754   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9755     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9756           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9757     {
9758       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9759     }
9760   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9761     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9762           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9763     {
9764       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9765     }
9766   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9767     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9768           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9769     {
9770       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9771     }
9772
9773   // make quadratic elements instead of linear ones
9774
9775   SMESHDS_Mesh* meshDS = GetMeshDS();
9776   SMESHDS_SubMesh* smDS = 0;
9777   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9778   {
9779     const SMDS_MeshElement* elem = *eIt;
9780     if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9781       continue;
9782
9783     const int id                   = elem->GetID();
9784     const SMDSAbs_ElementType type = elem->GetType();
9785     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9786
9787     if ( !smDS || !smDS->Contains( elem ))
9788       smDS = meshDS->MeshElements( elem->getshapeId() );
9789     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9790
9791     SMDS_MeshElement * newElem = 0;
9792     switch( nodes.size() )
9793     {
9794     case 4: // cases for most frequently used element types go first (for optimization)
9795       if ( type == SMDSAbs_Volume )
9796         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9797       else
9798         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9799       break;
9800     case 8:
9801       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9802                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9803       break;
9804     case 3:
9805       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9806       break;
9807     case 2:
9808       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9809       break;
9810     case 5:
9811       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9812                                  nodes[4], id, theForce3d);
9813       break;
9814     case 6:
9815       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9816                                  nodes[4], nodes[5], id, theForce3d);
9817       break;
9818     default:;
9819     }
9820     ReplaceElemInGroups( elem, newElem, meshDS);
9821     if( newElem && smDS )
9822       smDS->AddElement( newElem );
9823   }
9824
9825   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9826   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9827     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9828     helper.FixQuadraticElements( myError );
9829   }
9830 }
9831
9832 //=======================================================================
9833 /*!
9834  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9835  * \return int - nb of checked elements
9836  */
9837 //=======================================================================
9838
9839 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9840                                      SMDS_ElemIteratorPtr theItr,
9841                                      const int            theShapeID)
9842 {
9843   int nbElem = 0;
9844   SMESHDS_Mesh* meshDS = GetMeshDS();
9845
9846   while( theItr->more() )
9847   {
9848     const SMDS_MeshElement* elem = theItr->next();
9849     nbElem++;
9850     if( elem && elem->IsQuadratic())
9851     {
9852       int id                    = elem->GetID();
9853       int nbCornerNodes         = elem->NbCornerNodes();
9854       SMDSAbs_ElementType aType = elem->GetType();
9855
9856       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9857
9858       //remove a quadratic element
9859       if ( !theSm || !theSm->Contains( elem ))
9860         theSm = meshDS->MeshElements( elem->getshapeId() );
9861       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9862
9863       // remove medium nodes
9864       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9865         if ( nodes[i]->NbInverseElements() == 0 )
9866           meshDS->RemoveFreeNode( nodes[i], theSm );
9867
9868       // add a linear element
9869       nodes.resize( nbCornerNodes );
9870       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9871       ReplaceElemInGroups(elem, newElem, meshDS);
9872       if( theSm && newElem )
9873         theSm->AddElement( newElem );
9874     }
9875   }
9876   return nbElem;
9877 }
9878
9879 //=======================================================================
9880 //function : ConvertFromQuadratic
9881 //purpose  :
9882 //=======================================================================
9883
9884 bool SMESH_MeshEditor::ConvertFromQuadratic()
9885 {
9886   int nbCheckedElems = 0;
9887   if ( myMesh->HasShapeToMesh() )
9888   {
9889     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9890     {
9891       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9892       while ( smIt->more() ) {
9893         SMESH_subMesh* sm = smIt->next();
9894         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9895           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9896       }
9897     }
9898   }
9899
9900   int totalNbElems =
9901     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9902   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9903   {
9904     SMESHDS_SubMesh *aSM = 0;
9905     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9906   }
9907
9908   return true;
9909 }
9910
9911 namespace
9912 {
9913   //================================================================================
9914   /*!
9915    * \brief Return true if all medium nodes of the element are in the node set
9916    */
9917   //================================================================================
9918
9919   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9920   {
9921     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9922       if ( !nodeSet.count( elem->GetNode(i) ))
9923         return false;
9924     return true;
9925   }
9926 }
9927
9928 //================================================================================
9929 /*!
9930  * \brief Makes given elements linear
9931  */
9932 //================================================================================
9933
9934 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9935 {
9936   if ( theElements.empty() ) return;
9937
9938   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9939   set<int> mediumNodeIDs;
9940   TIDSortedElemSet::iterator eIt = theElements.begin();
9941   for ( ; eIt != theElements.end(); ++eIt )
9942   {
9943     const SMDS_MeshElement* e = *eIt;
9944     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9945       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9946   }
9947
9948   // replace given elements by linear ones
9949   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9950   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9951   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9952
9953   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9954   // except those elements sharing medium nodes of quadratic element whose medium nodes
9955   // are not all in mediumNodeIDs
9956
9957   // get remaining medium nodes
9958   TIDSortedNodeSet mediumNodes;
9959   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9960   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9961     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9962       mediumNodes.insert( mediumNodes.end(), n );
9963
9964   // find more quadratic elements to convert
9965   TIDSortedElemSet moreElemsToConvert;
9966   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9967   for ( ; nIt != mediumNodes.end(); ++nIt )
9968   {
9969     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9970     while ( invIt->more() )
9971     {
9972       const SMDS_MeshElement* e = invIt->next();
9973       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9974       {
9975         // find a more complex element including e and
9976         // whose medium nodes are not in mediumNodes
9977         bool complexFound = false;
9978         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9979         {
9980           SMDS_ElemIteratorPtr invIt2 =
9981             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9982           while ( invIt2->more() )
9983           {
9984             const SMDS_MeshElement* eComplex = invIt2->next();
9985             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9986             {
9987               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9988               if ( nbCommonNodes == e->NbNodes())
9989               {
9990                 complexFound = true;
9991                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9992                 break;
9993               }
9994             }
9995           }
9996         }
9997         if ( !complexFound )
9998           moreElemsToConvert.insert( e );
9999       }
10000     }
10001   }
10002   elemIt = SMDS_ElemIteratorPtr
10003     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10004   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10005 }
10006
10007 //=======================================================================
10008 //function : SewSideElements
10009 //purpose  :
10010 //=======================================================================
10011
10012 SMESH_MeshEditor::Sew_Error
10013 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10014                                    TIDSortedElemSet&    theSide2,
10015                                    const SMDS_MeshNode* theFirstNode1,
10016                                    const SMDS_MeshNode* theFirstNode2,
10017                                    const SMDS_MeshNode* theSecondNode1,
10018                                    const SMDS_MeshNode* theSecondNode2)
10019 {
10020   myLastCreatedElems.Clear();
10021   myLastCreatedNodes.Clear();
10022
10023   MESSAGE ("::::SewSideElements()");
10024   if ( theSide1.size() != theSide2.size() )
10025     return SEW_DIFF_NB_OF_ELEMENTS;
10026
10027   Sew_Error aResult = SEW_OK;
10028   // Algo:
10029   // 1. Build set of faces representing each side
10030   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10031   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10032
10033   // =======================================================================
10034   // 1. Build set of faces representing each side:
10035   // =======================================================================
10036   // a. build set of nodes belonging to faces
10037   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10038   // c. create temporary faces representing side of volumes if correspondent
10039   //    face does not exist
10040
10041   SMESHDS_Mesh* aMesh = GetMeshDS();
10042   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10043   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10044   TIDSortedElemSet             faceSet1, faceSet2;
10045   set<const SMDS_MeshElement*> volSet1,  volSet2;
10046   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10047   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10048   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10049   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10050   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10051   int iSide, iFace, iNode;
10052
10053   list<const SMDS_MeshElement* > tempFaceList;
10054   for ( iSide = 0; iSide < 2; iSide++ ) {
10055     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10056     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10057     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10058     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10059     set<const SMDS_MeshElement*>::iterator vIt;
10060     TIDSortedElemSet::iterator eIt;
10061     set<const SMDS_MeshNode*>::iterator    nIt;
10062
10063     // check that given nodes belong to given elements
10064     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10065     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10066     int firstIndex = -1, secondIndex = -1;
10067     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10068       const SMDS_MeshElement* elem = *eIt;
10069       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10070       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10071       if ( firstIndex > -1 && secondIndex > -1 ) break;
10072     }
10073     if ( firstIndex < 0 || secondIndex < 0 ) {
10074       // we can simply return until temporary faces created
10075       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10076     }
10077
10078     // -----------------------------------------------------------
10079     // 1a. Collect nodes of existing faces
10080     //     and build set of face nodes in order to detect missing
10081     //     faces corresponding to sides of volumes
10082     // -----------------------------------------------------------
10083
10084     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10085
10086     // loop on the given element of a side
10087     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10088       //const SMDS_MeshElement* elem = *eIt;
10089       const SMDS_MeshElement* elem = *eIt;
10090       if ( elem->GetType() == SMDSAbs_Face ) {
10091         faceSet->insert( elem );
10092         set <const SMDS_MeshNode*> faceNodeSet;
10093         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10094         while ( nodeIt->more() ) {
10095           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10096           nodeSet->insert( n );
10097           faceNodeSet.insert( n );
10098         }
10099         setOfFaceNodeSet.insert( faceNodeSet );
10100       }
10101       else if ( elem->GetType() == SMDSAbs_Volume )
10102         volSet->insert( elem );
10103     }
10104     // ------------------------------------------------------------------------------
10105     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10106     // ------------------------------------------------------------------------------
10107
10108     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10109       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10110       while ( fIt->more() ) { // loop on faces sharing a node
10111         const SMDS_MeshElement* f = fIt->next();
10112         if ( faceSet->find( f ) == faceSet->end() ) {
10113           // check if all nodes are in nodeSet and
10114           // complete setOfFaceNodeSet if they are
10115           set <const SMDS_MeshNode*> faceNodeSet;
10116           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10117           bool allInSet = true;
10118           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10119             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10120             if ( nodeSet->find( n ) == nodeSet->end() )
10121               allInSet = false;
10122             else
10123               faceNodeSet.insert( n );
10124           }
10125           if ( allInSet ) {
10126             faceSet->insert( f );
10127             setOfFaceNodeSet.insert( faceNodeSet );
10128           }
10129         }
10130       }
10131     }
10132
10133     // -------------------------------------------------------------------------
10134     // 1c. Create temporary faces representing sides of volumes if correspondent
10135     //     face does not exist
10136     // -------------------------------------------------------------------------
10137
10138     if ( !volSet->empty() ) {
10139       //int nodeSetSize = nodeSet->size();
10140
10141       // loop on given volumes
10142       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10143         SMDS_VolumeTool vol (*vIt);
10144         // loop on volume faces: find free faces
10145         // --------------------------------------
10146         list<const SMDS_MeshElement* > freeFaceList;
10147         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10148           if ( !vol.IsFreeFace( iFace ))
10149             continue;
10150           // check if there is already a face with same nodes in a face set
10151           const SMDS_MeshElement* aFreeFace = 0;
10152           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10153           int nbNodes = vol.NbFaceNodes( iFace );
10154           set <const SMDS_MeshNode*> faceNodeSet;
10155           vol.GetFaceNodes( iFace, faceNodeSet );
10156           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10157           if ( isNewFace ) {
10158             // no such a face is given but it still can exist, check it
10159             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10160             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10161           }
10162           if ( !aFreeFace ) {
10163             // create a temporary face
10164             if ( nbNodes == 3 ) {
10165               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10166               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10167             }
10168             else if ( nbNodes == 4 ) {
10169               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10170               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10171             }
10172             else {
10173               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10174               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10175               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10176             }
10177             if ( aFreeFace )
10178               tempFaceList.push_back( aFreeFace );
10179           }
10180
10181           if ( aFreeFace )
10182             freeFaceList.push_back( aFreeFace );
10183
10184         } // loop on faces of a volume
10185
10186         // choose one of several free faces of a volume
10187         // --------------------------------------------
10188         if ( freeFaceList.size() > 1 ) {
10189           // choose a face having max nb of nodes shared by other elems of a side
10190           int maxNbNodes = -1;
10191           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10192           while ( fIt != freeFaceList.end() ) { // loop on free faces
10193             int nbSharedNodes = 0;
10194             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10195             while ( nodeIt->more() ) { // loop on free face nodes
10196               const SMDS_MeshNode* n =
10197                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10198               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10199               while ( invElemIt->more() ) {
10200                 const SMDS_MeshElement* e = invElemIt->next();
10201                 nbSharedNodes += faceSet->count( e );
10202                 nbSharedNodes += elemSet->count( e );
10203               }
10204             }
10205             if ( nbSharedNodes > maxNbNodes ) {
10206               maxNbNodes = nbSharedNodes;
10207               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10208             }
10209             else if ( nbSharedNodes == maxNbNodes ) {
10210               fIt++;
10211             }
10212             else {
10213               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10214             }
10215           }
10216           if ( freeFaceList.size() > 1 )
10217           {
10218             // could not choose one face, use another way
10219             // choose a face most close to the bary center of the opposite side
10220             gp_XYZ aBC( 0., 0., 0. );
10221             set <const SMDS_MeshNode*> addedNodes;
10222             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10223             eIt = elemSet2->begin();
10224             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10225               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10226               while ( nodeIt->more() ) { // loop on free face nodes
10227                 const SMDS_MeshNode* n =
10228                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10229                 if ( addedNodes.insert( n ).second )
10230                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10231               }
10232             }
10233             aBC /= addedNodes.size();
10234             double minDist = DBL_MAX;
10235             fIt = freeFaceList.begin();
10236             while ( fIt != freeFaceList.end() ) { // loop on free faces
10237               double dist = 0;
10238               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10239               while ( nodeIt->more() ) { // loop on free face nodes
10240                 const SMDS_MeshNode* n =
10241                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10242                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10243                 dist += ( aBC - p ).SquareModulus();
10244               }
10245               if ( dist < minDist ) {
10246                 minDist = dist;
10247                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10248               }
10249               else
10250                 fIt = freeFaceList.erase( fIt++ );
10251             }
10252           }
10253         } // choose one of several free faces of a volume
10254
10255         if ( freeFaceList.size() == 1 ) {
10256           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10257           faceSet->insert( aFreeFace );
10258           // complete a node set with nodes of a found free face
10259           //           for ( iNode = 0; iNode < ; iNode++ )
10260           //             nodeSet->insert( fNodes[ iNode ] );
10261         }
10262
10263       } // loop on volumes of a side
10264
10265       //       // complete a set of faces if new nodes in a nodeSet appeared
10266       //       // ----------------------------------------------------------
10267       //       if ( nodeSetSize != nodeSet->size() ) {
10268       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10269       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10270       //           while ( fIt->more() ) { // loop on faces sharing a node
10271       //             const SMDS_MeshElement* f = fIt->next();
10272       //             if ( faceSet->find( f ) == faceSet->end() ) {
10273       //               // check if all nodes are in nodeSet and
10274       //               // complete setOfFaceNodeSet if they are
10275       //               set <const SMDS_MeshNode*> faceNodeSet;
10276       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10277       //               bool allInSet = true;
10278       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10279       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10280       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10281       //                   allInSet = false;
10282       //                 else
10283       //                   faceNodeSet.insert( n );
10284       //               }
10285       //               if ( allInSet ) {
10286       //                 faceSet->insert( f );
10287       //                 setOfFaceNodeSet.insert( faceNodeSet );
10288       //               }
10289       //             }
10290       //           }
10291       //         }
10292       //       }
10293     } // Create temporary faces, if there are volumes given
10294   } // loop on sides
10295
10296   if ( faceSet1.size() != faceSet2.size() ) {
10297     // delete temporary faces: they are in reverseElements of actual nodes
10298 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10299 //    while ( tmpFaceIt->more() )
10300 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10301 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10302 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10303 //      aMesh->RemoveElement(*tmpFaceIt);
10304     MESSAGE("Diff nb of faces");
10305     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10306   }
10307
10308   // ============================================================
10309   // 2. Find nodes to merge:
10310   //              bind a node to remove to a node to put instead
10311   // ============================================================
10312
10313   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10314   if ( theFirstNode1 != theFirstNode2 )
10315     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10316   if ( theSecondNode1 != theSecondNode2 )
10317     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10318
10319   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10320   set< long > linkIdSet; // links to process
10321   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10322
10323   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10324   list< NLink > linkList[2];
10325   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10326   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10327   // loop on links in linkList; find faces by links and append links
10328   // of the found faces to linkList
10329   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10330   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10331   {
10332     NLink link[] = { *linkIt[0], *linkIt[1] };
10333     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10334     if ( !linkIdSet.count( linkID ) )
10335       continue;
10336
10337     // by links, find faces in the face sets,
10338     // and find indices of link nodes in the found faces;
10339     // in a face set, there is only one or no face sharing a link
10340     // ---------------------------------------------------------------
10341
10342     const SMDS_MeshElement* face[] = { 0, 0 };
10343     vector<const SMDS_MeshNode*> fnodes[2];
10344     int iLinkNode[2][2];
10345     TIDSortedElemSet avoidSet;
10346     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10347       const SMDS_MeshNode* n1 = link[iSide].first;
10348       const SMDS_MeshNode* n2 = link[iSide].second;
10349       //cout << "Side " << iSide << " ";
10350       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10351       // find a face by two link nodes
10352       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10353                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10354       if ( face[ iSide ])
10355       {
10356         //cout << " F " << face[ iSide]->GetID() <<endl;
10357         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10358         // put face nodes to fnodes
10359         if ( face[ iSide ]->IsQuadratic() )
10360         {
10361           // use interlaced nodes iterator
10362           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10363           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10364           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10365           while ( nIter->more() )
10366             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10367         }
10368         else
10369         {
10370           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10371                                   face[ iSide ]->end_nodes() );
10372         }
10373         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10374       }
10375     }
10376
10377     // check similarity of elements of the sides
10378     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10379       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10380       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10381         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10382       }
10383       else {
10384         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10385       }
10386       break; // do not return because it's necessary to remove tmp faces
10387     }
10388
10389     // set nodes to merge
10390     // -------------------
10391
10392     if ( face[0] && face[1] )  {
10393       const int nbNodes = face[0]->NbNodes();
10394       if ( nbNodes != face[1]->NbNodes() ) {
10395         MESSAGE("Diff nb of face nodes");
10396         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10397         break; // do not return because it s necessary to remove tmp faces
10398       }
10399       bool reverse[] = { false, false }; // order of nodes in the link
10400       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10401         // analyse link orientation in faces
10402         int i1 = iLinkNode[ iSide ][ 0 ];
10403         int i2 = iLinkNode[ iSide ][ 1 ];
10404         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10405       }
10406       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10407       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10408       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10409       {
10410         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10411                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10412       }
10413
10414       // add other links of the faces to linkList
10415       // -----------------------------------------
10416
10417       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10418         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10419         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10420         if ( !iter_isnew.second ) { // already in a set: no need to process
10421           linkIdSet.erase( iter_isnew.first );
10422         }
10423         else // new in set == encountered for the first time: add
10424         {
10425           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10426           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10427           linkList[0].push_back ( NLink( n1, n2 ));
10428           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10429         }
10430       }
10431     } // 2 faces found
10432
10433     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10434       break;
10435
10436   } // loop on link lists
10437
10438   if ( aResult == SEW_OK &&
10439        ( //linkIt[0] != linkList[0].end() ||
10440          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10441     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10442              " " << (faceSetPtr[1]->empty()));
10443     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10444   }
10445
10446   // ====================================================================
10447   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10448   // ====================================================================
10449
10450   // delete temporary faces
10451 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10452 //  while ( tmpFaceIt->more() )
10453 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10454   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10455   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10456     aMesh->RemoveElement(*tmpFaceIt);
10457
10458   if ( aResult != SEW_OK)
10459     return aResult;
10460
10461   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10462   // loop on nodes replacement map
10463   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10464   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10465     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10466       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10467       nodeIDsToRemove.push_back( nToRemove->GetID() );
10468       // loop on elements sharing nToRemove
10469       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10470       while ( invElemIt->more() ) {
10471         const SMDS_MeshElement* e = invElemIt->next();
10472         // get a new suite of nodes: make replacement
10473         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10474         vector< const SMDS_MeshNode*> nodes( nbNodes );
10475         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10476         while ( nIt->more() ) {
10477           const SMDS_MeshNode* n =
10478             static_cast<const SMDS_MeshNode*>( nIt->next() );
10479           nnIt = nReplaceMap.find( n );
10480           if ( nnIt != nReplaceMap.end() ) {
10481             nbReplaced++;
10482             n = (*nnIt).second;
10483           }
10484           nodes[ i++ ] = n;
10485         }
10486         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10487         //         elemIDsToRemove.push_back( e->GetID() );
10488         //       else
10489         if ( nbReplaced )
10490           {
10491             SMDSAbs_ElementType etyp = e->GetType();
10492             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10493             if (newElem)
10494               {
10495                 myLastCreatedElems.Append(newElem);
10496                 AddToSameGroups(newElem, e, aMesh);
10497                 int aShapeId = e->getshapeId();
10498                 if ( aShapeId )
10499                   {
10500                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10501                   }
10502               }
10503             aMesh->RemoveElement(e);
10504           }
10505       }
10506     }
10507
10508   Remove( nodeIDsToRemove, true );
10509
10510   return aResult;
10511 }
10512
10513 //================================================================================
10514 /*!
10515  * \brief Find corresponding nodes in two sets of faces
10516  * \param theSide1 - first face set
10517  * \param theSide2 - second first face
10518  * \param theFirstNode1 - a boundary node of set 1
10519  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10520  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10521  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10522  * \param nReplaceMap - output map of corresponding nodes
10523  * \return bool  - is a success or not
10524  */
10525 //================================================================================
10526
10527 #ifdef _DEBUG_
10528 //#define DEBUG_MATCHING_NODES
10529 #endif
10530
10531 SMESH_MeshEditor::Sew_Error
10532 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10533                                     set<const SMDS_MeshElement*>& theSide2,
10534                                     const SMDS_MeshNode*          theFirstNode1,
10535                                     const SMDS_MeshNode*          theFirstNode2,
10536                                     const SMDS_MeshNode*          theSecondNode1,
10537                                     const SMDS_MeshNode*          theSecondNode2,
10538                                     TNodeNodeMap &                nReplaceMap)
10539 {
10540   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10541
10542   nReplaceMap.clear();
10543   if ( theFirstNode1 != theFirstNode2 )
10544     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10545   if ( theSecondNode1 != theSecondNode2 )
10546     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10547
10548   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10549   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10550
10551   list< NLink > linkList[2];
10552   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10553   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10554
10555   // loop on links in linkList; find faces by links and append links
10556   // of the found faces to linkList
10557   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10558   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10559     NLink link[] = { *linkIt[0], *linkIt[1] };
10560     if ( linkSet.find( link[0] ) == linkSet.end() )
10561       continue;
10562
10563     // by links, find faces in the face sets,
10564     // and find indices of link nodes in the found faces;
10565     // in a face set, there is only one or no face sharing a link
10566     // ---------------------------------------------------------------
10567
10568     const SMDS_MeshElement* face[] = { 0, 0 };
10569     list<const SMDS_MeshNode*> notLinkNodes[2];
10570     //bool reverse[] = { false, false }; // order of notLinkNodes
10571     int nbNodes[2];
10572     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10573     {
10574       const SMDS_MeshNode* n1 = link[iSide].first;
10575       const SMDS_MeshNode* n2 = link[iSide].second;
10576       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10577       set< const SMDS_MeshElement* > facesOfNode1;
10578       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10579       {
10580         // during a loop of the first node, we find all faces around n1,
10581         // during a loop of the second node, we find one face sharing both n1 and n2
10582         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10583         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10584         while ( fIt->more() ) { // loop on faces sharing a node
10585           const SMDS_MeshElement* f = fIt->next();
10586           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10587               ! facesOfNode1.insert( f ).second ) // f encounters twice
10588           {
10589             if ( face[ iSide ] ) {
10590               MESSAGE( "2 faces per link " );
10591               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10592             }
10593             face[ iSide ] = f;
10594             faceSet->erase( f );
10595
10596             // get not link nodes
10597             int nbN = f->NbNodes();
10598             if ( f->IsQuadratic() )
10599               nbN /= 2;
10600             nbNodes[ iSide ] = nbN;
10601             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10602             int i1 = f->GetNodeIndex( n1 );
10603             int i2 = f->GetNodeIndex( n2 );
10604             int iEnd = nbN, iBeg = -1, iDelta = 1;
10605             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10606             if ( reverse ) {
10607               std::swap( iEnd, iBeg ); iDelta = -1;
10608             }
10609             int i = i2;
10610             while ( true ) {
10611               i += iDelta;
10612               if ( i == iEnd ) i = iBeg + iDelta;
10613               if ( i == i1 ) break;
10614               nodes.push_back ( f->GetNode( i ) );
10615             }
10616           }
10617         }
10618       }
10619     }
10620     // check similarity of elements of the sides
10621     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10622       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10623       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10624         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10625       }
10626       else {
10627         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10628       }
10629     }
10630
10631     // set nodes to merge
10632     // -------------------
10633
10634     if ( face[0] && face[1] )  {
10635       if ( nbNodes[0] != nbNodes[1] ) {
10636         MESSAGE("Diff nb of face nodes");
10637         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10638       }
10639 #ifdef DEBUG_MATCHING_NODES
10640       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10641                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10642                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10643 #endif
10644       int nbN = nbNodes[0];
10645       {
10646         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10647         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10648         for ( int i = 0 ; i < nbN - 2; ++i ) {
10649 #ifdef DEBUG_MATCHING_NODES
10650           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10651 #endif
10652           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10653         }
10654       }
10655
10656       // add other links of the face 1 to linkList
10657       // -----------------------------------------
10658
10659       const SMDS_MeshElement* f0 = face[0];
10660       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10661       for ( int i = 0; i < nbN; i++ )
10662       {
10663         const SMDS_MeshNode* n2 = f0->GetNode( i );
10664         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10665           linkSet.insert( SMESH_TLink( n1, n2 ));
10666         if ( !iter_isnew.second ) { // already in a set: no need to process
10667           linkSet.erase( iter_isnew.first );
10668         }
10669         else // new in set == encountered for the first time: add
10670         {
10671 #ifdef DEBUG_MATCHING_NODES
10672           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10673                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10674 #endif
10675           linkList[0].push_back ( NLink( n1, n2 ));
10676           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10677         }
10678         n1 = n2;
10679       }
10680     } // 2 faces found
10681   } // loop on link lists
10682
10683   return SEW_OK;
10684 }
10685
10686 //================================================================================
10687 /*!
10688   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10689   \param theElems - the list of elements (edges or faces) to be replicated
10690   The nodes for duplication could be found from these elements
10691   \param theNodesNot - list of nodes to NOT replicate
10692   \param theAffectedElems - the list of elements (cells and edges) to which the
10693   replicated nodes should be associated to.
10694   \return TRUE if operation has been completed successfully, FALSE otherwise
10695 */
10696 //================================================================================
10697
10698 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10699                                     const TIDSortedElemSet& theNodesNot,
10700                                     const TIDSortedElemSet& theAffectedElems )
10701 {
10702   myLastCreatedElems.Clear();
10703   myLastCreatedNodes.Clear();
10704
10705   if ( theElems.size() == 0 )
10706     return false;
10707
10708   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10709   if ( !aMeshDS )
10710     return false;
10711
10712   bool res = false;
10713   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10714   // duplicate elements and nodes
10715   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10716   // replce nodes by duplications
10717   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10718   return res;
10719 }
10720
10721 //================================================================================
10722 /*!
10723   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10724   \param theMeshDS - mesh instance
10725   \param theElems - the elements replicated or modified (nodes should be changed)
10726   \param theNodesNot - nodes to NOT replicate
10727   \param theNodeNodeMap - relation of old node to new created node
10728   \param theIsDoubleElem - flag os to replicate element or modify
10729   \return TRUE if operation has been completed successfully, FALSE otherwise
10730 */
10731 //================================================================================
10732
10733 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10734                                     const TIDSortedElemSet& theElems,
10735                                     const TIDSortedElemSet& theNodesNot,
10736                                     std::map< const SMDS_MeshNode*,
10737                                     const SMDS_MeshNode* >& theNodeNodeMap,
10738                                     const bool theIsDoubleElem )
10739 {
10740   MESSAGE("doubleNodes");
10741   // iterate on through element and duplicate them (by nodes duplication)
10742   bool res = false;
10743   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10744   for ( ;  elemItr != theElems.end(); ++elemItr )
10745   {
10746     const SMDS_MeshElement* anElem = *elemItr;
10747     if (!anElem)
10748       continue;
10749
10750     bool isDuplicate = false;
10751     // duplicate nodes to duplicate element
10752     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10753     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10754     int ind = 0;
10755     while ( anIter->more() )
10756     {
10757
10758       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10759       SMDS_MeshNode* aNewNode = aCurrNode;
10760       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10761         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10762       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10763       {
10764         // duplicate node
10765         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10766         theNodeNodeMap[ aCurrNode ] = aNewNode;
10767         myLastCreatedNodes.Append( aNewNode );
10768       }
10769       isDuplicate |= (aCurrNode != aNewNode);
10770       newNodes[ ind++ ] = aNewNode;
10771     }
10772     if ( !isDuplicate )
10773       continue;
10774
10775     if ( theIsDoubleElem )
10776       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10777     else
10778       {
10779       MESSAGE("ChangeElementNodes");
10780       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10781       }
10782     res = true;
10783   }
10784   return res;
10785 }
10786
10787 //================================================================================
10788 /*!
10789   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10790   \param theNodes - identifiers of nodes to be doubled
10791   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10792          nodes. If list of element identifiers is empty then nodes are doubled but
10793          they not assigned to elements
10794   \return TRUE if operation has been completed successfully, FALSE otherwise
10795 */
10796 //================================================================================
10797
10798 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10799                                     const std::list< int >& theListOfModifiedElems )
10800 {
10801   MESSAGE("DoubleNodes");
10802   myLastCreatedElems.Clear();
10803   myLastCreatedNodes.Clear();
10804
10805   if ( theListOfNodes.size() == 0 )
10806     return false;
10807
10808   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10809   if ( !aMeshDS )
10810     return false;
10811
10812   // iterate through nodes and duplicate them
10813
10814   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10815
10816   std::list< int >::const_iterator aNodeIter;
10817   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10818   {
10819     int aCurr = *aNodeIter;
10820     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10821     if ( !aNode )
10822       continue;
10823
10824     // duplicate node
10825
10826     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10827     if ( aNewNode )
10828     {
10829       anOldNodeToNewNode[ aNode ] = aNewNode;
10830       myLastCreatedNodes.Append( aNewNode );
10831     }
10832   }
10833
10834   // Create map of new nodes for modified elements
10835
10836   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10837
10838   std::list< int >::const_iterator anElemIter;
10839   for ( anElemIter = theListOfModifiedElems.begin();
10840         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10841   {
10842     int aCurr = *anElemIter;
10843     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10844     if ( !anElem )
10845       continue;
10846
10847     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10848
10849     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10850     int ind = 0;
10851     while ( anIter->more() )
10852     {
10853       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10854       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10855       {
10856         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10857         aNodeArr[ ind++ ] = aNewNode;
10858       }
10859       else
10860         aNodeArr[ ind++ ] = aCurrNode;
10861     }
10862     anElemToNodes[ anElem ] = aNodeArr;
10863   }
10864
10865   // Change nodes of elements
10866
10867   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10868     anElemToNodesIter = anElemToNodes.begin();
10869   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10870   {
10871     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10872     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10873     if ( anElem )
10874       {
10875       MESSAGE("ChangeElementNodes");
10876       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10877       }
10878   }
10879
10880   return true;
10881 }
10882
10883 namespace {
10884
10885   //================================================================================
10886   /*!
10887   \brief Check if element located inside shape
10888   \return TRUE if IN or ON shape, FALSE otherwise
10889   */
10890   //================================================================================
10891
10892   template<class Classifier>
10893   bool isInside(const SMDS_MeshElement* theElem,
10894                 Classifier&             theClassifier,
10895                 const double            theTol)
10896   {
10897     gp_XYZ centerXYZ (0, 0, 0);
10898     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10899     while (aNodeItr->more())
10900       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10901
10902     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10903     theClassifier.Perform(aPnt, theTol);
10904     TopAbs_State aState = theClassifier.State();
10905     return (aState == TopAbs_IN || aState == TopAbs_ON );
10906   }
10907
10908   //================================================================================
10909   /*!
10910    * \brief Classifier of the 3D point on the TopoDS_Face
10911    *        with interaface suitable for isInside()
10912    */
10913   //================================================================================
10914
10915   struct _FaceClassifier
10916   {
10917     Extrema_ExtPS       _extremum;
10918     BRepAdaptor_Surface _surface;
10919     TopAbs_State        _state;
10920
10921     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10922     {
10923       _extremum.Initialize( _surface,
10924                             _surface.FirstUParameter(), _surface.LastUParameter(),
10925                             _surface.FirstVParameter(), _surface.LastVParameter(),
10926                             _surface.Tolerance(), _surface.Tolerance() );
10927     }
10928     void Perform(const gp_Pnt& aPnt, double theTol)
10929     {
10930       _state = TopAbs_OUT;
10931       _extremum.Perform(aPnt);
10932       if ( _extremum.IsDone() )
10933         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10934 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10935           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10936 #else
10937           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10938 #endif
10939     }
10940     TopAbs_State State() const
10941     {
10942       return _state;
10943     }
10944   };
10945 }
10946
10947 //================================================================================
10948 /*!
10949   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10950   This method is the first step of DoubleNodeElemGroupsInRegion.
10951   \param theElems - list of groups of elements (edges or faces) to be replicated
10952   \param theNodesNot - list of groups of nodes not to replicated
10953   \param theShape - shape to detect affected elements (element which geometric center
10954          located on or inside shape).
10955          The replicated nodes should be associated to affected elements.
10956   \return groups of affected elements
10957   \sa DoubleNodeElemGroupsInRegion()
10958  */
10959 //================================================================================
10960
10961 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10962                                                    const TIDSortedElemSet& theNodesNot,
10963                                                    const TopoDS_Shape&     theShape,
10964                                                    TIDSortedElemSet&       theAffectedElems)
10965 {
10966   if ( theShape.IsNull() )
10967     return false;
10968
10969   const double aTol = Precision::Confusion();
10970   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10971   auto_ptr<_FaceClassifier>              aFaceClassifier;
10972   if ( theShape.ShapeType() == TopAbs_SOLID )
10973   {
10974     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10975     bsc3d->PerformInfinitePoint(aTol);
10976   }
10977   else if (theShape.ShapeType() == TopAbs_FACE )
10978   {
10979     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10980   }
10981
10982   // iterates on indicated elements and get elements by back references from their nodes
10983   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10984   for ( ;  elemItr != theElems.end(); ++elemItr )
10985   {
10986     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10987     if (!anElem)
10988       continue;
10989
10990     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10991     while ( nodeItr->more() )
10992     {
10993       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10994       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10995         continue;
10996       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10997       while ( backElemItr->more() )
10998       {
10999         const SMDS_MeshElement* curElem = backElemItr->next();
11000         if ( curElem && theElems.find(curElem) == theElems.end() &&
11001              ( bsc3d.get() ?
11002                isInside( curElem, *bsc3d, aTol ) :
11003                isInside( curElem, *aFaceClassifier, aTol )))
11004           theAffectedElems.insert( curElem );
11005       }
11006     }
11007   }
11008   return true;
11009 }
11010
11011 //================================================================================
11012 /*!
11013   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11014   \param theElems - group of of elements (edges or faces) to be replicated
11015   \param theNodesNot - group of nodes not to replicate
11016   \param theShape - shape to detect affected elements (element which geometric center
11017   located on or inside shape).
11018   The replicated nodes should be associated to affected elements.
11019   \return TRUE if operation has been completed successfully, FALSE otherwise
11020 */
11021 //================================================================================
11022
11023 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11024                                             const TIDSortedElemSet& theNodesNot,
11025                                             const TopoDS_Shape&     theShape )
11026 {
11027   if ( theShape.IsNull() )
11028     return false;
11029
11030   const double aTol = Precision::Confusion();
11031   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11032   auto_ptr<_FaceClassifier>              aFaceClassifier;
11033   if ( theShape.ShapeType() == TopAbs_SOLID )
11034   {
11035     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11036     bsc3d->PerformInfinitePoint(aTol);
11037   }
11038   else if (theShape.ShapeType() == TopAbs_FACE )
11039   {
11040     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11041   }
11042
11043   // iterates on indicated elements and get elements by back references from their nodes
11044   TIDSortedElemSet anAffected;
11045   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11046   for ( ;  elemItr != theElems.end(); ++elemItr )
11047   {
11048     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11049     if (!anElem)
11050       continue;
11051
11052     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11053     while ( nodeItr->more() )
11054     {
11055       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11056       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11057         continue;
11058       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11059       while ( backElemItr->more() )
11060       {
11061         const SMDS_MeshElement* curElem = backElemItr->next();
11062         if ( curElem && theElems.find(curElem) == theElems.end() &&
11063              ( bsc3d.get() ?
11064                isInside( curElem, *bsc3d, aTol ) :
11065                isInside( curElem, *aFaceClassifier, aTol )))
11066           anAffected.insert( curElem );
11067       }
11068     }
11069   }
11070   return DoubleNodes( theElems, theNodesNot, anAffected );
11071 }
11072
11073 /*!
11074  *  \brief compute an oriented angle between two planes defined by four points.
11075  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11076  *  @param p0 base of the rotation axe
11077  *  @param p1 extremity of the rotation axe
11078  *  @param g1 belongs to the first plane
11079  *  @param g2 belongs to the second plane
11080  */
11081 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11082 {
11083 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11084 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11085 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11086 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11087   gp_Vec vref(p0, p1);
11088   gp_Vec v1(p0, g1);
11089   gp_Vec v2(p0, g2);
11090   gp_Vec n1 = vref.Crossed(v1);
11091   gp_Vec n2 = vref.Crossed(v2);
11092   return n2.AngleWithRef(n1, vref);
11093 }
11094
11095 /*!
11096  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11097  * The list of groups must describe a partition of the mesh volumes.
11098  * The nodes of the internal faces at the boundaries of the groups are doubled.
11099  * In option, the internal faces are replaced by flat elements.
11100  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11101  * The flat elements are stored in groups of volumes.
11102  * @param theElems - list of groups of volumes, where a group of volume is a set of
11103  * SMDS_MeshElements sorted by Id.
11104  * @param createJointElems - if TRUE, create the elements
11105  * @return TRUE if operation has been completed successfully, FALSE otherwise
11106  */
11107 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11108                                                      bool createJointElems)
11109 {
11110   MESSAGE("----------------------------------------------");
11111   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11112   MESSAGE("----------------------------------------------");
11113
11114   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11115   meshDS->BuildDownWardConnectivity(true);
11116   CHRONO(50);
11117   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11118
11119   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11120   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11121   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11122
11123   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11124   std::map<int,int>celldom; // cell vtkId --> domain
11125   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11126   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11127   faceDomains.clear();
11128   celldom.clear();
11129   cellDomains.clear();
11130   nodeDomains.clear();
11131   std::map<int,int> emptyMap;
11132   std::set<int> emptySet;
11133   emptyMap.clear();
11134
11135   for (int idom = 0; idom < theElems.size(); idom++)
11136     {
11137
11138       // --- build a map (face to duplicate --> volume to modify)
11139       //     with all the faces shared by 2 domains (group of elements)
11140       //     and corresponding volume of this domain, for each shared face.
11141       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11142
11143       //MESSAGE("Domain " << idom);
11144       const TIDSortedElemSet& domain = theElems[idom];
11145       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11146       for (; elemItr != domain.end(); ++elemItr)
11147         {
11148           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11149           if (!anElem)
11150             continue;
11151           int vtkId = anElem->getVtkId();
11152           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11153           int neighborsVtkIds[NBMAXNEIGHBORS];
11154           int downIds[NBMAXNEIGHBORS];
11155           unsigned char downTypes[NBMAXNEIGHBORS];
11156           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11157           for (int n = 0; n < nbNeighbors; n++)
11158             {
11159               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11160               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11161               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11162                 {
11163                   DownIdType face(downIds[n], downTypes[n]);
11164                   if (!faceDomains.count(face))
11165                     faceDomains[face] = emptyMap; // create an empty entry for face
11166                   if (!faceDomains[face].count(idom))
11167                     {
11168                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11169                       celldom[vtkId] = idom;
11170                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11171                     }
11172                 }
11173             }
11174         }
11175     }
11176
11177   //MESSAGE("Number of shared faces " << faceDomains.size());
11178   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11179
11180   // --- explore the shared faces domain by domain,
11181   //     explore the nodes of the face and see if they belong to a cell in the domain,
11182   //     which has only a node or an edge on the border (not a shared face)
11183
11184   for (int idomain = 0; idomain < theElems.size(); idomain++)
11185     {
11186       //MESSAGE("Domain " << idomain);
11187       const TIDSortedElemSet& domain = theElems[idomain];
11188       itface = faceDomains.begin();
11189       for (; itface != faceDomains.end(); ++itface)
11190         {
11191           std::map<int, int> domvol = itface->second;
11192           if (!domvol.count(idomain))
11193             continue;
11194           DownIdType face = itface->first;
11195           //MESSAGE(" --- face " << face.cellId);
11196           std::set<int> oldNodes;
11197           oldNodes.clear();
11198           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11199           std::set<int>::iterator itn = oldNodes.begin();
11200           for (; itn != oldNodes.end(); ++itn)
11201             {
11202               int oldId = *itn;
11203               //MESSAGE("     node " << oldId);
11204               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11205               for (int i=0; i<l.ncells; i++)
11206                 {
11207                   int vtkId = l.cells[i];
11208                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11209                   if (!domain.count(anElem))
11210                     continue;
11211                   int vtkType = grid->GetCellType(vtkId);
11212                   int downId = grid->CellIdToDownId(vtkId);
11213                   if (downId < 0)
11214                     {
11215                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11216                       continue; // not OK at this stage of the algorithm:
11217                                 //no cells created after BuildDownWardConnectivity
11218                     }
11219                   DownIdType aCell(downId, vtkType);
11220                   if (!cellDomains.count(aCell))
11221                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11222                   cellDomains[aCell][idomain] = vtkId;
11223                   celldom[vtkId] = idomain;
11224                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11225                 }
11226             }
11227         }
11228     }
11229
11230   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11231   //     for each shared face, get the nodes
11232   //     for each node, for each domain of the face, create a clone of the node
11233
11234   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11235   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11236   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11237
11238   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11239   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11240   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11241
11242   for (int idomain = 0; idomain < theElems.size(); idomain++)
11243     {
11244       itface = faceDomains.begin();
11245       for (; itface != faceDomains.end(); ++itface)
11246         {
11247           std::map<int, int> domvol = itface->second;
11248           if (!domvol.count(idomain))
11249             continue;
11250           DownIdType face = itface->first;
11251           //MESSAGE(" --- face " << face.cellId);
11252           std::set<int> oldNodes;
11253           oldNodes.clear();
11254           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11255           std::set<int>::iterator itn = oldNodes.begin();
11256           for (; itn != oldNodes.end(); ++itn)
11257             {
11258               int oldId = *itn;
11259               //MESSAGE("-+-+-a node " << oldId);
11260               if (!nodeDomains.count(oldId))
11261                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11262               if (nodeDomains[oldId].empty())
11263                 {
11264                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11265                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11266                 }
11267               std::map<int, int>::iterator itdom = domvol.begin();
11268               for (; itdom != domvol.end(); ++itdom)
11269                 {
11270                   int idom = itdom->first;
11271                   //MESSAGE("         domain " << idom);
11272                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11273                     {
11274                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11275                         {
11276                           vector<int> orderedDoms;
11277                           //MESSAGE("multiple node " << oldId);
11278                           if (mutipleNodes.count(oldId))
11279                             orderedDoms = mutipleNodes[oldId];
11280                           else
11281                             {
11282                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11283                               for (; it != nodeDomains[oldId].end(); ++it)
11284                                 orderedDoms.push_back(it->first);
11285                             }
11286                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11287                           //stringstream txt;
11288                           //for (int i=0; i<orderedDoms.size(); i++)
11289                           //  txt << orderedDoms[i] << " ";
11290                           //MESSAGE("orderedDoms " << txt.str());
11291                           mutipleNodes[oldId] = orderedDoms;
11292                         }
11293                       double *coords = grid->GetPoint(oldId);
11294                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11295                       int newId = newNode->getVtkId();
11296                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11297                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11298                     }
11299                 }
11300             }
11301         }
11302     }
11303
11304   for (int idomain = 0; idomain < theElems.size(); idomain++)
11305     {
11306       itface = faceDomains.begin();
11307       for (; itface != faceDomains.end(); ++itface)
11308         {
11309           std::map<int, int> domvol = itface->second;
11310           if (!domvol.count(idomain))
11311             continue;
11312           DownIdType face = itface->first;
11313           //MESSAGE(" --- face " << face.cellId);
11314           std::set<int> oldNodes;
11315           oldNodes.clear();
11316           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11317           int nbMultipleNodes = 0;
11318           std::set<int>::iterator itn = oldNodes.begin();
11319           for (; itn != oldNodes.end(); ++itn)
11320             {
11321               int oldId = *itn;
11322               if (mutipleNodes.count(oldId))
11323                 nbMultipleNodes++;
11324             }
11325           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11326             {
11327               //MESSAGE("multiple Nodes detected on a shared face");
11328               int downId = itface->first.cellId;
11329               unsigned char cellType = itface->first.cellType;
11330               // --- shared edge or shared face ?
11331               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11332                 {
11333                   int nodes[3];
11334                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11335                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11336                     if (mutipleNodes.count(nodes[i]))
11337                       if (!mutipleNodesToFace.count(nodes[i]))
11338                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11339                 }
11340               else // shared face (between two volumes)
11341                 {
11342                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11343                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11344                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11345                   for (int ie =0; ie < nbEdges; ie++)
11346                     {
11347                       int nodes[3];
11348                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11349                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11350                         {
11351                           vector<int> vn0 = mutipleNodes[nodes[0]];
11352                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11353                           vector<int> doms;
11354                           for (int i0 = 0; i0 < vn0.size(); i0++)
11355                             for (int i1 = 0; i1 < vn1.size(); i1++)
11356                               if (vn0[i0] == vn1[i1])
11357                                 doms.push_back(vn0[i0]);
11358                           if (doms.size() >2)
11359                             {
11360                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11361                               double *coords = grid->GetPoint(nodes[0]);
11362                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11363                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11364                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11365                               gp_Pnt gref;
11366                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11367                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11368                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11369                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11370                               for (int id=0; id < doms.size(); id++)
11371                                 {
11372                                   int idom = doms[id];
11373                                   for (int ivol=0; ivol<nbvol; ivol++)
11374                                     {
11375                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11376                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11377                                       if (theElems[idom].count(elem))
11378                                         {
11379                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11380                                           domvol[idom] = svol;
11381                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11382                                           double values[3];
11383                                           vtkIdType npts = 0;
11384                                           vtkIdType* pts = 0;
11385                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11386                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11387                                           if (id ==0)
11388                                             {
11389                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11390                                               angleDom[idom] = 0;
11391                                             }
11392                                           else
11393                                             {
11394                                               gp_Pnt g(values[0], values[1], values[2]);
11395                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11396                                               //MESSAGE("  angle=" << angleDom[idom]);
11397                                             }
11398                                           break;
11399                                         }
11400                                     }
11401                                 }
11402                               map<double, int> sortedDom; // sort domains by angle
11403                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11404                                 sortedDom[ia->second] = ia->first;
11405                               vector<int> vnodes;
11406                               vector<int> vdom;
11407                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11408                                 {
11409                                   vdom.push_back(ib->second);
11410                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11411                                 }
11412                               for (int ino = 0; ino < nbNodes; ino++)
11413                                 vnodes.push_back(nodes[ino]);
11414                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11415                             }
11416                         }
11417                     }
11418                 }
11419             }
11420         }
11421     }
11422
11423   // --- iterate on shared faces (volumes to modify, face to extrude)
11424   //     get node id's of the face (id SMDS = id VTK)
11425   //     create flat element with old and new nodes if requested
11426
11427   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11428   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11429
11430   std::map<int, std::map<long,int> > nodeQuadDomains;
11431   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11432
11433   if (createJointElems)
11434     {
11435       int idg;
11436       string joints2DName = "joints2D";
11437       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11438       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11439       string joints3DName = "joints3D";
11440       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11441       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11442
11443       itface = faceDomains.begin();
11444       for (; itface != faceDomains.end(); ++itface)
11445         {
11446           DownIdType face = itface->first;
11447           std::set<int> oldNodes;
11448           std::set<int>::iterator itn;
11449           oldNodes.clear();
11450           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11451
11452           std::map<int, int> domvol = itface->second;
11453           std::map<int, int>::iterator itdom = domvol.begin();
11454           int dom1 = itdom->first;
11455           int vtkVolId = itdom->second;
11456           itdom++;
11457           int dom2 = itdom->first;
11458           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11459                                                              nodeQuadDomains);
11460           stringstream grpname;
11461           grpname << "j_";
11462           if (dom1 < dom2)
11463             grpname << dom1 << "_" << dom2;
11464           else
11465             grpname << dom2 << "_" << dom1;
11466           string namegrp = grpname.str();
11467           if (!mapOfJunctionGroups.count(namegrp))
11468             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11469           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11470           if (sgrp)
11471             sgrp->Add(vol->GetID());
11472           if (vol->GetType() == SMDSAbs_Volume)
11473             joints3DGrp->Add(vol->GetID());
11474           else if (vol->GetType() == SMDSAbs_Face)
11475             joints2DGrp->Add(vol->GetID());
11476         }
11477     }
11478
11479   // --- create volumes on multiple domain intersection if requested
11480   //     iterate on mutipleNodesToFace
11481   //     iterate on edgesMultiDomains
11482
11483   if (createJointElems)
11484     {
11485       // --- iterate on mutipleNodesToFace
11486
11487       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11488       for (; itn != mutipleNodesToFace.end(); ++itn)
11489         {
11490           int node = itn->first;
11491           vector<int> orderDom = itn->second;
11492           vector<vtkIdType> orderedNodes;
11493           for (int idom = 0; idom <orderDom.size(); idom++)
11494             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11495             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11496
11497             stringstream grpname;
11498             grpname << "m2j_";
11499             grpname << 0 << "_" << 0;
11500             int idg;
11501             string namegrp = grpname.str();
11502             if (!mapOfJunctionGroups.count(namegrp))
11503               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11504             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11505             if (sgrp)
11506               sgrp->Add(face->GetID());
11507         }
11508
11509       // --- iterate on edgesMultiDomains
11510
11511       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11512       for (; ite != edgesMultiDomains.end(); ++ite)
11513         {
11514           vector<int> nodes = ite->first;
11515           vector<int> orderDom = ite->second;
11516           vector<vtkIdType> orderedNodes;
11517           if (nodes.size() == 2)
11518             {
11519               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11520               for (int ino=0; ino < nodes.size(); ino++)
11521                 if (orderDom.size() == 3)
11522                   for (int idom = 0; idom <orderDom.size(); idom++)
11523                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11524                 else
11525                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11526                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11527               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11528
11529               int idg;
11530               string namegrp = "jointsMultiples";
11531               if (!mapOfJunctionGroups.count(namegrp))
11532                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11533               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11534               if (sgrp)
11535                 sgrp->Add(vol->GetID());
11536             }
11537           else
11538             {
11539               INFOS("Quadratic multiple joints not implemented");
11540               // TODO quadratic nodes
11541             }
11542         }
11543     }
11544
11545   // --- list the explicit faces and edges of the mesh that need to be modified,
11546   //     i.e. faces and edges built with one or more duplicated nodes.
11547   //     associate these faces or edges to their corresponding domain.
11548   //     only the first domain found is kept when a face or edge is shared
11549
11550   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11551   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11552   faceOrEdgeDom.clear();
11553   feDom.clear();
11554
11555   for (int idomain = 0; idomain < theElems.size(); idomain++)
11556     {
11557       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11558       for (; itnod != nodeDomains.end(); ++itnod)
11559         {
11560           int oldId = itnod->first;
11561           //MESSAGE("     node " << oldId);
11562           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11563           for (int i = 0; i < l.ncells; i++)
11564             {
11565               int vtkId = l.cells[i];
11566               int vtkType = grid->GetCellType(vtkId);
11567               int downId = grid->CellIdToDownId(vtkId);
11568               if (downId < 0)
11569                 continue; // new cells: not to be modified
11570               DownIdType aCell(downId, vtkType);
11571               int volParents[1000];
11572               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11573               for (int j = 0; j < nbvol; j++)
11574                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11575                   if (!feDom.count(vtkId))
11576                     {
11577                       feDom[vtkId] = idomain;
11578                       faceOrEdgeDom[aCell] = emptyMap;
11579                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11580                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11581                       //        << " type " << vtkType << " downId " << downId);
11582                     }
11583             }
11584         }
11585     }
11586
11587   // --- iterate on shared faces (volumes to modify, face to extrude)
11588   //     get node id's of the face
11589   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11590
11591   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11592   for (int m=0; m<3; m++)
11593     {
11594       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11595       itface = (*amap).begin();
11596       for (; itface != (*amap).end(); ++itface)
11597         {
11598           DownIdType face = itface->first;
11599           std::set<int> oldNodes;
11600           std::set<int>::iterator itn;
11601           oldNodes.clear();
11602           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11603           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11604           std::map<int, int> localClonedNodeIds;
11605
11606           std::map<int, int> domvol = itface->second;
11607           std::map<int, int>::iterator itdom = domvol.begin();
11608           for (; itdom != domvol.end(); ++itdom)
11609             {
11610               int idom = itdom->first;
11611               int vtkVolId = itdom->second;
11612               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11613               localClonedNodeIds.clear();
11614               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11615                 {
11616                   int oldId = *itn;
11617                   if (nodeDomains[oldId].count(idom))
11618                     {
11619                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11620                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11621                     }
11622                 }
11623               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11624             }
11625         }
11626     }
11627
11628   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11629   grid->BuildLinks();
11630
11631   CHRONOSTOP(50);
11632   counters::stats();
11633   return true;
11634 }
11635
11636 /*!
11637  * \brief Double nodes on some external faces and create flat elements.
11638  * Flat elements are mainly used by some types of mechanic calculations.
11639  *
11640  * Each group of the list must be constituted of faces.
11641  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11642  * @param theElems - list of groups of faces, where a group of faces is a set of
11643  * SMDS_MeshElements sorted by Id.
11644  * @return TRUE if operation has been completed successfully, FALSE otherwise
11645  */
11646 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11647 {
11648   MESSAGE("-------------------------------------------------");
11649   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11650   MESSAGE("-------------------------------------------------");
11651
11652   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11653
11654   // --- For each group of faces
11655   //     duplicate the nodes, create a flat element based on the face
11656   //     replace the nodes of the faces by their clones
11657
11658   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11659   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11660   clonedNodes.clear();
11661   intermediateNodes.clear();
11662   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11663   mapOfJunctionGroups.clear();
11664
11665   for (int idom = 0; idom < theElems.size(); idom++)
11666     {
11667       const TIDSortedElemSet& domain = theElems[idom];
11668       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11669       for (; elemItr != domain.end(); ++elemItr)
11670         {
11671           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11672           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11673           if (!aFace)
11674             continue;
11675           // MESSAGE("aFace=" << aFace->GetID());
11676           bool isQuad = aFace->IsQuadratic();
11677           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11678
11679           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11680
11681           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11682           while (nodeIt->more())
11683             {
11684               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11685               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11686               if (isMedium)
11687                 ln2.push_back(node);
11688               else
11689                 ln0.push_back(node);
11690
11691               const SMDS_MeshNode* clone = 0;
11692               if (!clonedNodes.count(node))
11693                 {
11694                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11695                   clonedNodes[node] = clone;
11696                 }
11697               else
11698                 clone = clonedNodes[node];
11699
11700               if (isMedium)
11701                 ln3.push_back(clone);
11702               else
11703                 ln1.push_back(clone);
11704
11705               const SMDS_MeshNode* inter = 0;
11706               if (isQuad && (!isMedium))
11707                 {
11708                   if (!intermediateNodes.count(node))
11709                     {
11710                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11711                       intermediateNodes[node] = inter;
11712                     }
11713                   else
11714                     inter = intermediateNodes[node];
11715                   ln4.push_back(inter);
11716                 }
11717             }
11718
11719           // --- extrude the face
11720
11721           vector<const SMDS_MeshNode*> ln;
11722           SMDS_MeshVolume* vol = 0;
11723           vtkIdType aType = aFace->GetVtkType();
11724           switch (aType)
11725           {
11726             case VTK_TRIANGLE:
11727               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11728               // MESSAGE("vol prism " << vol->GetID());
11729               ln.push_back(ln1[0]);
11730               ln.push_back(ln1[1]);
11731               ln.push_back(ln1[2]);
11732               break;
11733             case VTK_QUAD:
11734               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11735               // MESSAGE("vol hexa " << vol->GetID());
11736               ln.push_back(ln1[0]);
11737               ln.push_back(ln1[1]);
11738               ln.push_back(ln1[2]);
11739               ln.push_back(ln1[3]);
11740               break;
11741             case VTK_QUADRATIC_TRIANGLE:
11742               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11743                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11744               // MESSAGE("vol quad prism " << vol->GetID());
11745               ln.push_back(ln1[0]);
11746               ln.push_back(ln1[1]);
11747               ln.push_back(ln1[2]);
11748               ln.push_back(ln3[0]);
11749               ln.push_back(ln3[1]);
11750               ln.push_back(ln3[2]);
11751               break;
11752             case VTK_QUADRATIC_QUAD:
11753 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11754 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11755 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11756               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11757                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11758                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11759               // MESSAGE("vol quad hexa " << vol->GetID());
11760               ln.push_back(ln1[0]);
11761               ln.push_back(ln1[1]);
11762               ln.push_back(ln1[2]);
11763               ln.push_back(ln1[3]);
11764               ln.push_back(ln3[0]);
11765               ln.push_back(ln3[1]);
11766               ln.push_back(ln3[2]);
11767               ln.push_back(ln3[3]);
11768               break;
11769             case VTK_POLYGON:
11770               break;
11771             default:
11772               break;
11773           }
11774
11775           if (vol)
11776             {
11777               stringstream grpname;
11778               grpname << "jf_";
11779               grpname << idom;
11780               int idg;
11781               string namegrp = grpname.str();
11782               if (!mapOfJunctionGroups.count(namegrp))
11783                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11784               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11785               if (sgrp)
11786                 sgrp->Add(vol->GetID());
11787             }
11788
11789           // --- modify the face
11790
11791           aFace->ChangeNodes(&ln[0], ln.size());
11792         }
11793     }
11794   return true;
11795 }
11796
11797 /*!
11798  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11799  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11800  *  groups of faces to remove inside the object, (idem edges).
11801  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11802  */
11803 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11804                                       const TopoDS_Shape& theShape,
11805                                       SMESH_NodeSearcher* theNodeSearcher,
11806                                       const char* groupName,
11807                                       std::vector<double>&   nodesCoords,
11808                                       std::vector<std::vector<int> >& listOfListOfNodes)
11809 {
11810   MESSAGE("--------------------------------");
11811   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11812   MESSAGE("--------------------------------");
11813
11814   // --- zone of volumes to remove is given :
11815   //     1 either by a geom shape (one or more vertices) and a radius,
11816   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11817   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11818   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11819   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11820   //     defined by it's name.
11821
11822   SMESHDS_GroupBase* groupDS = 0;
11823   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11824   while ( groupIt->more() )
11825     {
11826       groupDS = 0;
11827       SMESH_Group * group = groupIt->next();
11828       if ( !group ) continue;
11829       groupDS = group->GetGroupDS();
11830       if ( !groupDS || groupDS->IsEmpty() ) continue;
11831       std::string grpName = group->GetName();
11832       //MESSAGE("grpName=" << grpName);
11833       if (grpName == groupName)
11834         break;
11835       else
11836         groupDS = 0;
11837     }
11838
11839   bool isNodeGroup = false;
11840   bool isNodeCoords = false;
11841   if (groupDS)
11842     {
11843       if (groupDS->GetType() != SMDSAbs_Node)
11844         return;
11845       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11846     }
11847
11848   if (nodesCoords.size() > 0)
11849     isNodeCoords = true; // a list o nodes given by their coordinates
11850   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11851
11852   // --- define groups to build
11853
11854   int idg; // --- group of SMDS volumes
11855   string grpvName = groupName;
11856   grpvName += "_vol";
11857   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11858   if (!grp)
11859     {
11860       MESSAGE("group not created " << grpvName);
11861       return;
11862     }
11863   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11864
11865   int idgs; // --- group of SMDS faces on the skin
11866   string grpsName = groupName;
11867   grpsName += "_skin";
11868   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11869   if (!grps)
11870     {
11871       MESSAGE("group not created " << grpsName);
11872       return;
11873     }
11874   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11875
11876   int idgi; // --- group of SMDS faces internal (several shapes)
11877   string grpiName = groupName;
11878   grpiName += "_internalFaces";
11879   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11880   if (!grpi)
11881     {
11882       MESSAGE("group not created " << grpiName);
11883       return;
11884     }
11885   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11886
11887   int idgei; // --- group of SMDS faces internal (several shapes)
11888   string grpeiName = groupName;
11889   grpeiName += "_internalEdges";
11890   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11891   if (!grpei)
11892     {
11893       MESSAGE("group not created " << grpeiName);
11894       return;
11895     }
11896   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11897
11898   // --- build downward connectivity
11899
11900   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11901   meshDS->BuildDownWardConnectivity(true);
11902   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11903
11904   // --- set of volumes detected inside
11905
11906   std::set<int> setOfInsideVol;
11907   std::set<int> setOfVolToCheck;
11908
11909   std::vector<gp_Pnt> gpnts;
11910   gpnts.clear();
11911
11912   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11913     {
11914       MESSAGE("group of nodes provided");
11915       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11916       while ( elemIt->more() )
11917         {
11918           const SMDS_MeshElement* elem = elemIt->next();
11919           if (!elem)
11920             continue;
11921           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11922           if (!node)
11923             continue;
11924           SMDS_MeshElement* vol = 0;
11925           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11926           while (volItr->more())
11927             {
11928               vol = (SMDS_MeshElement*)volItr->next();
11929               setOfInsideVol.insert(vol->getVtkId());
11930               sgrp->Add(vol->GetID());
11931             }
11932         }
11933     }
11934   else if (isNodeCoords)
11935     {
11936       MESSAGE("list of nodes coordinates provided");
11937       int i = 0;
11938       int k = 0;
11939       while (i < nodesCoords.size()-2)
11940         {
11941           double x = nodesCoords[i++];
11942           double y = nodesCoords[i++];
11943           double z = nodesCoords[i++];
11944           gp_Pnt p = gp_Pnt(x, y ,z);
11945           gpnts.push_back(p);
11946           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11947         }
11948     }
11949   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11950     {
11951       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11952       TopTools_IndexedMapOfShape vertexMap;
11953       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11954       gp_Pnt p = gp_Pnt(0,0,0);
11955       if (vertexMap.Extent() < 1)
11956         return;
11957
11958       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11959         {
11960           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11961           p = BRep_Tool::Pnt(vertex);
11962           gpnts.push_back(p);
11963           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11964         }
11965     }
11966
11967   if (gpnts.size() > 0)
11968     {
11969       int nodeId = 0;
11970       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11971       if (startNode)
11972         nodeId = startNode->GetID();
11973       MESSAGE("nodeId " << nodeId);
11974
11975       double radius2 = radius*radius;
11976       MESSAGE("radius2 " << radius2);
11977
11978       // --- volumes on start node
11979
11980       setOfVolToCheck.clear();
11981       SMDS_MeshElement* startVol = 0;
11982       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11983       while (volItr->more())
11984         {
11985           startVol = (SMDS_MeshElement*)volItr->next();
11986           setOfVolToCheck.insert(startVol->getVtkId());
11987         }
11988       if (setOfVolToCheck.empty())
11989         {
11990           MESSAGE("No volumes found");
11991           return;
11992         }
11993
11994       // --- starting with central volumes then their neighbors, check if they are inside
11995       //     or outside the domain, until no more new neighbor volume is inside.
11996       //     Fill the group of inside volumes
11997
11998       std::map<int, double> mapOfNodeDistance2;
11999       mapOfNodeDistance2.clear();
12000       std::set<int> setOfOutsideVol;
12001       while (!setOfVolToCheck.empty())
12002         {
12003           std::set<int>::iterator it = setOfVolToCheck.begin();
12004           int vtkId = *it;
12005           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12006           bool volInside = false;
12007           vtkIdType npts = 0;
12008           vtkIdType* pts = 0;
12009           grid->GetCellPoints(vtkId, npts, pts);
12010           for (int i=0; i<npts; i++)
12011             {
12012               double distance2 = 0;
12013               if (mapOfNodeDistance2.count(pts[i]))
12014                 {
12015                   distance2 = mapOfNodeDistance2[pts[i]];
12016                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12017                 }
12018               else
12019                 {
12020                   double *coords = grid->GetPoint(pts[i]);
12021                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12022                   distance2 = 1.E40;
12023                   for (int j=0; j<gpnts.size(); j++)
12024                     {
12025                       double d2 = aPoint.SquareDistance(gpnts[j]);
12026                       if (d2 < distance2)
12027                         {
12028                           distance2 = d2;
12029                           if (distance2 < radius2)
12030                             break;
12031                         }
12032                     }
12033                   mapOfNodeDistance2[pts[i]] = distance2;
12034                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12035                 }
12036               if (distance2 < radius2)
12037                 {
12038                   volInside = true; // one or more nodes inside the domain
12039                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12040                   break;
12041                 }
12042             }
12043           if (volInside)
12044             {
12045               setOfInsideVol.insert(vtkId);
12046               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12047               int neighborsVtkIds[NBMAXNEIGHBORS];
12048               int downIds[NBMAXNEIGHBORS];
12049               unsigned char downTypes[NBMAXNEIGHBORS];
12050               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12051               for (int n = 0; n < nbNeighbors; n++)
12052                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12053                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12054             }
12055           else
12056             {
12057               setOfOutsideVol.insert(vtkId);
12058               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12059             }
12060           setOfVolToCheck.erase(vtkId);
12061         }
12062     }
12063
12064   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12065   //     If yes, add the volume to the inside set
12066
12067   bool addedInside = true;
12068   std::set<int> setOfVolToReCheck;
12069   while (addedInside)
12070     {
12071       MESSAGE(" --------------------------- re check");
12072       addedInside = false;
12073       std::set<int>::iterator itv = setOfInsideVol.begin();
12074       for (; itv != setOfInsideVol.end(); ++itv)
12075         {
12076           int vtkId = *itv;
12077           int neighborsVtkIds[NBMAXNEIGHBORS];
12078           int downIds[NBMAXNEIGHBORS];
12079           unsigned char downTypes[NBMAXNEIGHBORS];
12080           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12081           for (int n = 0; n < nbNeighbors; n++)
12082             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12083               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12084         }
12085       setOfVolToCheck = setOfVolToReCheck;
12086       setOfVolToReCheck.clear();
12087       while  (!setOfVolToCheck.empty())
12088         {
12089           std::set<int>::iterator it = setOfVolToCheck.begin();
12090           int vtkId = *it;
12091           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12092             {
12093               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12094               int countInside = 0;
12095               int neighborsVtkIds[NBMAXNEIGHBORS];
12096               int downIds[NBMAXNEIGHBORS];
12097               unsigned char downTypes[NBMAXNEIGHBORS];
12098               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12099               for (int n = 0; n < nbNeighbors; n++)
12100                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12101                   countInside++;
12102               MESSAGE("countInside " << countInside);
12103               if (countInside > 1)
12104                 {
12105                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12106                   setOfInsideVol.insert(vtkId);
12107                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12108                   addedInside = true;
12109                 }
12110               else
12111                 setOfVolToReCheck.insert(vtkId);
12112             }
12113           setOfVolToCheck.erase(vtkId);
12114         }
12115     }
12116
12117   // --- map of Downward faces at the boundary, inside the global volume
12118   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12119   //     fill group of SMDS faces inside the volume (when several volume shapes)
12120   //     fill group of SMDS faces on the skin of the global volume (if skin)
12121
12122   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12123   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12124   std::set<int>::iterator it = setOfInsideVol.begin();
12125   for (; it != setOfInsideVol.end(); ++it)
12126     {
12127       int vtkId = *it;
12128       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12129       int neighborsVtkIds[NBMAXNEIGHBORS];
12130       int downIds[NBMAXNEIGHBORS];
12131       unsigned char downTypes[NBMAXNEIGHBORS];
12132       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12133       for (int n = 0; n < nbNeighbors; n++)
12134         {
12135           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12136           if (neighborDim == 3)
12137             {
12138               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12139                 {
12140                   DownIdType face(downIds[n], downTypes[n]);
12141                   boundaryFaces[face] = vtkId;
12142                 }
12143               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12144               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12145               if (vtkFaceId >= 0)
12146                 {
12147                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12148                   // find also the smds edges on this face
12149                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12150                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12151                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12152                   for (int i = 0; i < nbEdges; i++)
12153                     {
12154                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12155                       if (vtkEdgeId >= 0)
12156                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12157                     }
12158                 }
12159             }
12160           else if (neighborDim == 2) // skin of the volume
12161             {
12162               DownIdType face(downIds[n], downTypes[n]);
12163               skinFaces[face] = vtkId;
12164               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12165               if (vtkFaceId >= 0)
12166                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12167             }
12168         }
12169     }
12170
12171   // --- identify the edges constituting the wire of each subshape on the skin
12172   //     define polylines with the nodes of edges, equivalent to wires
12173   //     project polylines on subshapes, and partition, to get geom faces
12174
12175   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12176   std::set<int> emptySet;
12177   emptySet.clear();
12178   std::set<int> shapeIds;
12179
12180   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12181   while (itelem->more())
12182     {
12183       const SMDS_MeshElement *elem = itelem->next();
12184       int shapeId = elem->getshapeId();
12185       int vtkId = elem->getVtkId();
12186       if (!shapeIdToVtkIdSet.count(shapeId))
12187         {
12188           shapeIdToVtkIdSet[shapeId] = emptySet;
12189           shapeIds.insert(shapeId);
12190         }
12191       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12192     }
12193
12194   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12195   std::set<DownIdType, DownIdCompare> emptyEdges;
12196   emptyEdges.clear();
12197
12198   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12199   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12200     {
12201       int shapeId = itShape->first;
12202       MESSAGE(" --- Shape ID --- "<< shapeId);
12203       shapeIdToEdges[shapeId] = emptyEdges;
12204
12205       std::vector<int> nodesEdges;
12206
12207       std::set<int>::iterator its = itShape->second.begin();
12208       for (; its != itShape->second.end(); ++its)
12209         {
12210           int vtkId = *its;
12211           MESSAGE("     " << vtkId);
12212           int neighborsVtkIds[NBMAXNEIGHBORS];
12213           int downIds[NBMAXNEIGHBORS];
12214           unsigned char downTypes[NBMAXNEIGHBORS];
12215           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12216           for (int n = 0; n < nbNeighbors; n++)
12217             {
12218               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12219                 continue;
12220               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12221               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12222               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12223                 {
12224                   DownIdType edge(downIds[n], downTypes[n]);
12225                   if (!shapeIdToEdges[shapeId].count(edge))
12226                     {
12227                       shapeIdToEdges[shapeId].insert(edge);
12228                       int vtkNodeId[3];
12229                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12230                       nodesEdges.push_back(vtkNodeId[0]);
12231                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12232                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12233                     }
12234                 }
12235             }
12236         }
12237
12238       std::list<int> order;
12239       order.clear();
12240       if (nodesEdges.size() > 0)
12241         {
12242           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12243           nodesEdges[0] = -1;
12244           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12245           nodesEdges[1] = -1; // do not reuse this edge
12246           bool found = true;
12247           while (found)
12248             {
12249               int nodeTofind = order.back(); // try first to push back
12250               int i = 0;
12251               for (i = 0; i<nodesEdges.size(); i++)
12252                 if (nodesEdges[i] == nodeTofind)
12253                   break;
12254               if (i == nodesEdges.size())
12255                 found = false; // no follower found on back
12256               else
12257                 {
12258                   if (i%2) // odd ==> use the previous one
12259                     if (nodesEdges[i-1] < 0)
12260                       found = false;
12261                     else
12262                       {
12263                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12264                         nodesEdges[i-1] = -1;
12265                       }
12266                   else // even ==> use the next one
12267                     if (nodesEdges[i+1] < 0)
12268                       found = false;
12269                     else
12270                       {
12271                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12272                         nodesEdges[i+1] = -1;
12273                       }
12274                 }
12275               if (found)
12276                 continue;
12277               // try to push front
12278               found = true;
12279               nodeTofind = order.front(); // try to push front
12280               for (i = 0; i<nodesEdges.size(); i++)
12281                 if (nodesEdges[i] == nodeTofind)
12282                   break;
12283               if (i == nodesEdges.size())
12284                 {
12285                   found = false; // no predecessor found on front
12286                   continue;
12287                 }
12288               if (i%2) // odd ==> use the previous one
12289                 if (nodesEdges[i-1] < 0)
12290                   found = false;
12291                 else
12292                   {
12293                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12294                     nodesEdges[i-1] = -1;
12295                   }
12296               else // even ==> use the next one
12297                 if (nodesEdges[i+1] < 0)
12298                   found = false;
12299                 else
12300                   {
12301                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12302                     nodesEdges[i+1] = -1;
12303                   }
12304             }
12305         }
12306
12307
12308       std::vector<int> nodes;
12309       nodes.push_back(shapeId);
12310       std::list<int>::iterator itl = order.begin();
12311       for (; itl != order.end(); itl++)
12312         {
12313           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12314           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12315         }
12316       listOfListOfNodes.push_back(nodes);
12317     }
12318
12319   //     partition geom faces with blocFissure
12320   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12321   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12322
12323   return;
12324 }
12325
12326
12327 //================================================================================
12328 /*!
12329  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12330  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12331  * \return TRUE if operation has been completed successfully, FALSE otherwise
12332  */
12333 //================================================================================
12334
12335 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12336 {
12337   // iterates on volume elements and detect all free faces on them
12338   SMESHDS_Mesh* aMesh = GetMeshDS();
12339   if (!aMesh)
12340     return false;
12341   //bool res = false;
12342   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12343   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12344   while(vIt->more())
12345   {
12346     const SMDS_MeshVolume* volume = vIt->next();
12347     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12348     vTool.SetExternalNormal();
12349     //const bool isPoly = volume->IsPoly();
12350     const int iQuad = volume->IsQuadratic();
12351     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12352     {
12353       if (!vTool.IsFreeFace(iface))
12354         continue;
12355       nbFree++;
12356       vector<const SMDS_MeshNode *> nodes;
12357       int nbFaceNodes = vTool.NbFaceNodes(iface);
12358       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12359       int inode = 0;
12360       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12361         nodes.push_back(faceNodes[inode]);
12362       if (iQuad) { // add medium nodes
12363         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12364           nodes.push_back(faceNodes[inode]);
12365         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12366           nodes.push_back(faceNodes[8]);
12367       }
12368       // add new face based on volume nodes
12369       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12370         nbExisted++;
12371         continue; // face already exsist
12372       }
12373       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12374       nbCreated++;
12375     }
12376   }
12377   return ( nbFree==(nbExisted+nbCreated) );
12378 }
12379
12380 namespace
12381 {
12382   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12383   {
12384     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12385       return n;
12386     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12387   }
12388 }
12389 //================================================================================
12390 /*!
12391  * \brief Creates missing boundary elements
12392  *  \param elements - elements whose boundary is to be checked
12393  *  \param dimension - defines type of boundary elements to create
12394  *  \param group - a group to store created boundary elements in
12395  *  \param targetMesh - a mesh to store created boundary elements in
12396  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12397  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12398  *                                boundary elements will be copied into the targetMesh
12399  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12400  *                                boundary elements will be added into the new group
12401  *  \param aroundElements - if true, elements will be created on boundary of given
12402  *                          elements else, on boundary of the whole mesh.
12403  * \return nb of added boundary elements
12404  */
12405 //================================================================================
12406
12407 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12408                                        Bnd_Dimension           dimension,
12409                                        SMESH_Group*            group/*=0*/,
12410                                        SMESH_Mesh*             targetMesh/*=0*/,
12411                                        bool                    toCopyElements/*=false*/,
12412                                        bool                    toCopyExistingBoundary/*=false*/,
12413                                        bool                    toAddExistingBondary/*= false*/,
12414                                        bool                    aroundElements/*= false*/)
12415 {
12416   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12417   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12418   // hope that all elements are of the same type, do not check them all
12419   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12420     throw SALOME_Exception(LOCALIZED("wrong element type"));
12421
12422   if ( !targetMesh )
12423     toCopyElements = toCopyExistingBoundary = false;
12424
12425   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12426   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12427   int nbAddedBnd = 0;
12428
12429   // editor adding present bnd elements and optionally holding elements to add to the group
12430   SMESH_MeshEditor* presentEditor;
12431   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12432   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12433
12434   SMESH_MesherHelper helper( *myMesh );
12435   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12436   SMDS_VolumeTool vTool;
12437   TIDSortedElemSet avoidSet;
12438   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12439   int inode;
12440
12441   typedef vector<const SMDS_MeshNode*> TConnectivity;
12442
12443   SMDS_ElemIteratorPtr eIt;
12444   if (elements.empty())
12445     eIt = aMesh->elementsIterator(elemType);
12446   else
12447     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12448
12449   while (eIt->more())
12450   {
12451     const SMDS_MeshElement* elem = eIt->next();
12452     const int iQuad = elem->IsQuadratic();
12453
12454     // ------------------------------------------------------------------------------------
12455     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12456     // ------------------------------------------------------------------------------------
12457     vector<const SMDS_MeshElement*> presentBndElems;
12458     vector<TConnectivity>           missingBndElems;
12459     TConnectivity nodes;
12460     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12461     {
12462       vTool.SetExternalNormal();
12463       const SMDS_MeshElement* otherVol = 0;
12464       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12465       {
12466         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12467              ( !aroundElements || elements.count( otherVol )))
12468           continue;
12469         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12470         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12471         if ( missType == SMDSAbs_Edge ) // boundary edges
12472         {
12473           nodes.resize( 2+iQuad );
12474           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12475           {
12476             for ( int j = 0; j < nodes.size(); ++j )
12477               nodes[j] =nn[i+j];
12478             if ( const SMDS_MeshElement* edge =
12479                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12480               presentBndElems.push_back( edge );
12481             else
12482               missingBndElems.push_back( nodes );
12483           }
12484         }
12485         else // boundary face
12486         {
12487           nodes.clear();
12488           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12489             nodes.push_back( nn[inode] );
12490           if (iQuad) // add medium nodes
12491             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12492               nodes.push_back( nn[inode] );
12493           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12494           if ( iCenter > 0 )
12495             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12496
12497           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12498                                                                SMDSAbs_Face, /*noMedium=*/false ))
12499             presentBndElems.push_back( f );
12500           else
12501             missingBndElems.push_back( nodes );
12502
12503           if ( targetMesh != myMesh )
12504           {
12505             // add 1D elements on face boundary to be added to a new mesh
12506             const SMDS_MeshElement* edge;
12507             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12508             {
12509               if ( iQuad )
12510                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12511               else
12512                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12513               if ( edge && avoidSet.insert( edge ).second )
12514                 presentBndElems.push_back( edge );
12515             }
12516           }
12517         }
12518       }
12519     }
12520     else                     // elem is a face ------------------------------------------
12521     {
12522       avoidSet.clear(), avoidSet.insert( elem );
12523       int nbNodes = elem->NbCornerNodes();
12524       nodes.resize( 2 /*+ iQuad*/);
12525       for ( int i = 0; i < nbNodes; i++ )
12526       {
12527         nodes[0] = elem->GetNode(i);
12528         nodes[1] = elem->GetNode((i+1)%nbNodes);
12529         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12530           continue; // not free link
12531
12532         //if ( iQuad )
12533         //nodes[2] = elem->GetNode( i + nbNodes );
12534         if ( const SMDS_MeshElement* edge =
12535              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12536           presentBndElems.push_back( edge );
12537         else
12538           missingBndElems.push_back( nodes );
12539       }
12540     }
12541
12542     // ---------------------------------
12543     // 2. Add missing boundary elements
12544     // ---------------------------------
12545     if ( targetMesh != myMesh )
12546       // instead of making a map of nodes in this mesh and targetMesh,
12547       // we create nodes with same IDs.
12548       for ( int i = 0; i < missingBndElems.size(); ++i )
12549       {
12550         TConnectivity& srcNodes = missingBndElems[i];
12551         TConnectivity  nodes( srcNodes.size() );
12552         for ( inode = 0; inode < nodes.size(); ++inode )
12553           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12554         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12555                                                                    missType,
12556                                                                    /*noMedium=*/false))
12557           continue;
12558         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12559         ++nbAddedBnd;
12560       }
12561     else
12562       for ( int i = 0; i < missingBndElems.size(); ++i )
12563       {
12564         TConnectivity& nodes = missingBndElems[i];
12565         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12566                                                                    missType,
12567                                                                    /*noMedium=*/false))
12568           continue;
12569         SMDS_MeshElement* elem =
12570           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12571         ++nbAddedBnd;
12572
12573         // try to set a new element to a shape
12574         if ( myMesh->HasShapeToMesh() )
12575         {
12576           bool ok = true;
12577           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12578           const int nbN = nodes.size() / (iQuad+1 );
12579           for ( inode = 0; inode < nbN && ok; ++inode )
12580           {
12581             pair<int, TopAbs_ShapeEnum> i_stype =
12582               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12583             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12584               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12585           }
12586           if ( ok && mediumShapes.size() > 1 )
12587           {
12588             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12589             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12590             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12591             {
12592               if (( ok = ( stype_i->first != stype_i_0.first )))
12593                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12594                                         aMesh->IndexToShape( stype_i_0.second ));
12595             }
12596           }
12597           if ( ok && mediumShapes.begin()->first == missShapeType )
12598             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12599         }
12600       }
12601
12602     // ----------------------------------
12603     // 3. Copy present boundary elements
12604     // ----------------------------------
12605     if ( toCopyExistingBoundary )
12606       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12607       {
12608         const SMDS_MeshElement* e = presentBndElems[i];
12609         TConnectivity nodes( e->NbNodes() );
12610         for ( inode = 0; inode < nodes.size(); ++inode )
12611           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12612         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12613       }
12614     else // store present elements to add them to a group
12615       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12616       {
12617         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12618       }
12619
12620   } // loop on given elements
12621
12622   // ---------------------------------------------
12623   // 4. Fill group with boundary elements
12624   // ---------------------------------------------
12625   if ( group )
12626   {
12627     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12628       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12629         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12630   }
12631   tgtEditor.myLastCreatedElems.Clear();
12632   tgtEditor2.myLastCreatedElems.Clear();
12633
12634   // -----------------------
12635   // 5. Copy given elements
12636   // -----------------------
12637   if ( toCopyElements && targetMesh != myMesh )
12638   {
12639     if (elements.empty())
12640       eIt = aMesh->elementsIterator(elemType);
12641     else
12642       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12643     while (eIt->more())
12644     {
12645       const SMDS_MeshElement* elem = eIt->next();
12646       TConnectivity nodes( elem->NbNodes() );
12647       for ( inode = 0; inode < nodes.size(); ++inode )
12648         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12649       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12650
12651       tgtEditor.myLastCreatedElems.Clear();
12652     }
12653   }
12654   return nbAddedBnd;
12655 }