Salome HOME
Merge from V6_main_20120808 08Aug12
[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 Add element
126  */
127 //=======================================================================
128
129 SMDS_MeshElement*
130 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
131                              const SMDSAbs_ElementType            type,
132                              const bool                           isPoly,
133                              const int                            ID,
134                              const double                         ballDiameter)
135 {
136   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
137   SMDS_MeshElement* e = 0;
138   int nbnode = node.size();
139   SMESHDS_Mesh* mesh = GetMeshDS();
140   switch ( type ) {
141   case SMDSAbs_Face:
142     if ( !isPoly ) {
143       if      (nbnode == 3) {
144         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
145         else           e = mesh->AddFace      (node[0], node[1], node[2] );
146       }
147       else if (nbnode == 4) {
148         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
149         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
150       }
151       else if (nbnode == 6) {
152         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
153                                                node[4], node[5], ID);
154         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
155                                                node[4], node[5] );
156       }
157       else if (nbnode == 8) {
158         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
159                                                node[4], node[5], node[6], node[7], ID);
160         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
161                                                node[4], node[5], node[6], node[7] );
162       }
163       else if (nbnode == 9) {
164         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
165                                                node[4], node[5], node[6], node[7], node[8], ID);
166         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
167                                                node[4], node[5], node[6], node[7], node[8] );
168       }
169     } else {
170       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
171       else           e = mesh->AddPolygonalFace      (node    );
172     }
173     break;
174
175   case SMDSAbs_Volume:
176     if ( !isPoly ) {
177       if      (nbnode == 4) {
178         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
179         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
180       }
181       else if (nbnode == 5) {
182         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
183                                                  node[4], ID);
184         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
185                                                  node[4] );
186       }
187       else if (nbnode == 6) {
188         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
189                                                  node[4], node[5], ID);
190         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
191                                                  node[4], node[5] );
192       }
193       else if (nbnode == 8) {
194         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
195                                                  node[4], node[5], node[6], node[7], ID);
196         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
197                                                  node[4], node[5], node[6], node[7] );
198       }
199       else if (nbnode == 10) {
200         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
201                                                  node[4], node[5], node[6], node[7],
202                                                  node[8], node[9], ID);
203         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
204                                                  node[4], node[5], node[6], node[7],
205                                                  node[8], node[9] );
206       }
207       else if (nbnode == 12) {
208         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
209                                                  node[4], node[5], node[6], node[7],
210                                                  node[8], node[9], node[10], node[11], ID);
211         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], node[6], node[7],
213                                                  node[8], node[9], node[10], node[11] );
214       }
215       else if (nbnode == 13) {
216         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
217                                                  node[4], node[5], node[6], node[7],
218                                                  node[8], node[9], node[10],node[11],
219                                                  node[12],ID);
220         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
221                                                  node[4], node[5], node[6], node[7],
222                                                  node[8], node[9], node[10],node[11],
223                                                  node[12] );
224       }
225       else if (nbnode == 15) {
226         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
227                                                  node[4], node[5], node[6], node[7],
228                                                  node[8], node[9], node[10],node[11],
229                                                  node[12],node[13],node[14],ID);
230         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
231                                                  node[4], node[5], node[6], node[7],
232                                                  node[8], node[9], node[10],node[11],
233                                                  node[12],node[13],node[14] );
234       }
235       else if (nbnode == 20) {
236         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
237                                                  node[4], node[5], node[6], node[7],
238                                                  node[8], node[9], node[10],node[11],
239                                                  node[12],node[13],node[14],node[15],
240                                                  node[16],node[17],node[18],node[19],ID);
241         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
242                                                  node[4], node[5], node[6], node[7],
243                                                  node[8], node[9], node[10],node[11],
244                                                  node[12],node[13],node[14],node[15],
245                                                  node[16],node[17],node[18],node[19] );
246       }
247       else if (nbnode == 27) {
248         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249                                                  node[4], node[5], node[6], node[7],
250                                                  node[8], node[9], node[10],node[11],
251                                                  node[12],node[13],node[14],node[15],
252                                                  node[16],node[17],node[18],node[19],
253                                                  node[20],node[21],node[22],node[23],
254                                                  node[24],node[25],node[26], ID);
255         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
256                                                  node[4], node[5], node[6], node[7],
257                                                  node[8], node[9], node[10],node[11],
258                                                  node[12],node[13],node[14],node[15],
259                                                  node[16],node[17],node[18],node[19],
260                                                  node[20],node[21],node[22],node[23],
261                                                  node[24],node[25],node[26] );
262       }
263     }
264     break;
265
266   case SMDSAbs_Edge:
267     if ( nbnode == 2 ) {
268       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
269       else           e = mesh->AddEdge      (node[0], node[1] );
270     }
271     else if ( nbnode == 3 ) {
272       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
273       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
274     }
275     break;
276
277   case SMDSAbs_0DElement:
278     if ( nbnode == 1 ) {
279       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
280       else           e = mesh->Add0DElement      (node[0] );
281     }
282     break;
283
284   case SMDSAbs_Node:
285     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
286     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
287     break;
288
289   case SMDSAbs_Ball:
290     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
291     else           e = mesh->AddBall      (node[0], ballDiameter);
292     break;
293
294   default:;
295   }
296   if ( e ) myLastCreatedElems.Append( e );
297   return e;
298 }
299
300 //=======================================================================
301 /*!
302  * \brief Add element
303  */
304 //=======================================================================
305
306 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
307                                                const SMDSAbs_ElementType type,
308                                                const bool                isPoly,
309                                                const int                 ID)
310 {
311   vector<const SMDS_MeshNode*> nodes;
312   nodes.reserve( nodeIDs.size() );
313   vector<int>::const_iterator id = nodeIDs.begin();
314   while ( id != nodeIDs.end() ) {
315     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
316       nodes.push_back( node );
317     else
318       return 0;
319   }
320   return AddElement( nodes, type, isPoly, ID );
321 }
322
323 //=======================================================================
324 //function : Remove
325 //purpose  : Remove a node or an element.
326 //           Modify a compute state of sub-meshes which become empty
327 //=======================================================================
328
329 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
330                               const bool         isNodes )
331 {
332   myLastCreatedElems.Clear();
333   myLastCreatedNodes.Clear();
334
335   SMESHDS_Mesh* aMesh = GetMeshDS();
336   set< SMESH_subMesh *> smmap;
337
338   int removed = 0;
339   list<int>::const_iterator it = theIDs.begin();
340   for ( ; it != theIDs.end(); it++ ) {
341     const SMDS_MeshElement * elem;
342     if ( isNodes )
343       elem = aMesh->FindNode( *it );
344     else
345       elem = aMesh->FindElement( *it );
346     if ( !elem )
347       continue;
348
349     // Notify VERTEX sub-meshes about modification
350     if ( isNodes ) {
351       const SMDS_MeshNode* node = cast2Node( elem );
352       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
353         if ( int aShapeID = node->getshapeId() )
354           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
355             smmap.insert( sm );
356     }
357     // Find sub-meshes to notify about modification
358     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
359     //     while ( nodeIt->more() ) {
360     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
361     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
362     //       if ( aPosition.get() ) {
363     //         if ( int aShapeID = aPosition->GetShapeId() ) {
364     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
365     //             smmap.insert( sm );
366     //         }
367     //       }
368     //     }
369
370     // Do remove
371     if ( isNodes )
372       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
373     else
374       aMesh->RemoveElement( elem );
375     removed++;
376   }
377
378   // Notify sub-meshes about modification
379   if ( !smmap.empty() ) {
380     set< SMESH_subMesh *>::iterator smIt;
381     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
382       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
383   }
384
385   //   // Check if the whole mesh becomes empty
386   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
387   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
388
389   return removed;
390 }
391
392 //=======================================================================
393 //function : FindShape
394 //purpose  : Return an index of the shape theElem is on
395 //           or zero if a shape not found
396 //=======================================================================
397
398 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
399 {
400   myLastCreatedElems.Clear();
401   myLastCreatedNodes.Clear();
402
403   SMESHDS_Mesh * aMesh = GetMeshDS();
404   if ( aMesh->ShapeToMesh().IsNull() )
405     return 0;
406
407   int aShapeID = theElem->getshapeId();
408   if ( aShapeID < 1 )
409     return 0;
410
411   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
412     if ( sm->Contains( theElem ))
413       return aShapeID;
414
415   if ( theElem->GetType() == SMDSAbs_Node ) {
416     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
417   }
418   else {
419     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
420   }
421
422   TopoDS_Shape aShape; // the shape a node of theElem is on
423   if ( theElem->GetType() != SMDSAbs_Node )
424   {
425     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
426     while ( nodeIt->more() ) {
427       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
428       if ((aShapeID = node->getshapeId()) > 0) {
429         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
430           if ( sm->Contains( theElem ))
431             return aShapeID;
432           if ( aShape.IsNull() )
433             aShape = aMesh->IndexToShape( aShapeID );
434         }
435       }
436     }
437   }
438
439   // None of nodes is on a proper shape,
440   // find the shape among ancestors of aShape on which a node is
441   if ( !aShape.IsNull() ) {
442     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
443     for ( ; ancIt.More(); ancIt.Next() ) {
444       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
445       if ( sm && sm->Contains( theElem ))
446         return aMesh->ShapeToIndex( ancIt.Value() );
447     }
448   }
449   else
450   {
451     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
452     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
453     for ( ; id_sm != id2sm.end(); ++id_sm )
454       if ( id_sm->second->Contains( theElem ))
455         return id_sm->first;
456   }
457
458   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
459   return 0;
460 }
461
462 //=======================================================================
463 //function : IsMedium
464 //purpose  :
465 //=======================================================================
466
467 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
468                                 const SMDSAbs_ElementType typeToCheck)
469 {
470   bool isMedium = false;
471   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
472   while (it->more() && !isMedium ) {
473     const SMDS_MeshElement* elem = it->next();
474     isMedium = elem->IsMediumNode(node);
475   }
476   return isMedium;
477 }
478
479 //=======================================================================
480 //function : ShiftNodesQuadTria
481 //purpose  : auxilary
482 //           Shift nodes in the array corresponded to quadratic triangle
483 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
484 //=======================================================================
485 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
486 {
487   const SMDS_MeshNode* nd1 = aNodes[0];
488   aNodes[0] = aNodes[1];
489   aNodes[1] = aNodes[2];
490   aNodes[2] = nd1;
491   const SMDS_MeshNode* nd2 = aNodes[3];
492   aNodes[3] = aNodes[4];
493   aNodes[4] = aNodes[5];
494   aNodes[5] = nd2;
495 }
496
497 //=======================================================================
498 //function : edgeConnectivity
499 //purpose  : auxilary 
500 //           return number of the edges connected with the theNode.
501 //           if theEdges has connections with the other type of the
502 //           elements, return -1 
503 //=======================================================================
504 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
505 {
506   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
507   int nb=0;
508   while(elemIt->more()) {
509     elemIt->next();
510     nb++;
511   }
512   return nb;
513 }
514
515
516 //=======================================================================
517 //function : GetNodesFromTwoTria
518 //purpose  : auxilary
519 //           Shift nodes in the array corresponded to quadratic triangle
520 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
521 //=======================================================================
522 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
523                                 const SMDS_MeshElement * theTria2,
524                                 const SMDS_MeshNode* N1[],
525                                 const SMDS_MeshNode* N2[])
526 {
527   SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
528   int i=0;
529   while(i<6) {
530     N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
531     i++;
532   }
533   if(it->more()) return false;
534   it = theTria2->nodesIterator();
535   i=0;
536   while(i<6) {
537     N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
538     i++;
539   }
540   if(it->more()) return false;
541
542   int sames[3] = {-1,-1,-1};
543   int nbsames = 0;
544   int j;
545   for(i=0; i<3; i++) {
546     for(j=0; j<3; j++) {
547       if(N1[i]==N2[j]) {
548         sames[i] = j;
549         nbsames++;
550         break;
551       }
552     }
553   }
554   if(nbsames!=2) return false;
555   if(sames[0]>-1) {
556     ShiftNodesQuadTria(N1);
557     if(sames[1]>-1) {
558       ShiftNodesQuadTria(N1);
559     }
560   }
561   i = sames[0] + sames[1] + sames[2];
562   for(; i<2; i++) {
563     ShiftNodesQuadTria(N2);
564   }
565   // now we receive following N1 and N2 (using numeration as above image)
566   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
567   // i.e. first nodes from both arrays determ new diagonal
568   return true;
569 }
570
571 //=======================================================================
572 //function : InverseDiag
573 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
574 //           but having other common link.
575 //           Return False if args are improper
576 //=======================================================================
577
578 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
579                                     const SMDS_MeshElement * theTria2 )
580 {
581   MESSAGE("InverseDiag");
582   myLastCreatedElems.Clear();
583   myLastCreatedNodes.Clear();
584
585   if (!theTria1 || !theTria2)
586     return false;
587
588   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
589   if (!F1) return false;
590   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
591   if (!F2) return false;
592   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
593       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
594
595     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
596     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
597     //    |/ |                                         | \|
598     //  B +--+ 2                                     B +--+ 2
599
600     // put nodes in array and find out indices of the same ones
601     const SMDS_MeshNode* aNodes [6];
602     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
603     int i = 0;
604     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
605     while ( it->more() ) {
606       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
607
608       if ( i > 2 ) // theTria2
609         // find same node of theTria1
610         for ( int j = 0; j < 3; j++ )
611           if ( aNodes[ i ] == aNodes[ j ]) {
612             sameInd[ j ] = i;
613             sameInd[ i ] = j;
614             break;
615           }
616       // next
617       i++;
618       if ( i == 3 ) {
619         if ( it->more() )
620           return false; // theTria1 is not a triangle
621         it = theTria2->nodesIterator();
622       }
623       if ( i == 6 && it->more() )
624         return false; // theTria2 is not a triangle
625     }
626
627     // find indices of 1,2 and of A,B in theTria1
628     int iA = 0, iB = 0, i1 = 0, i2 = 0;
629     for ( i = 0; i < 6; i++ ) {
630       if ( sameInd [ i ] == 0 ) {
631         if ( i < 3 ) i1 = i;
632         else         i2 = i;
633       }
634       else if (i < 3) {
635         if ( iA ) iB = i;
636         else      iA = i;
637       }
638     }
639     // nodes 1 and 2 should not be the same
640     if ( aNodes[ i1 ] == aNodes[ i2 ] )
641       return false;
642
643     // theTria1: A->2
644     aNodes[ iA ] = aNodes[ i2 ];
645     // theTria2: B->1
646     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
647
648     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
649     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
650
651     return true;
652
653   } // end if(F1 && F2)
654
655   // check case of quadratic faces
656   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
657     return false;
658   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
659     return false;
660
661   //       5
662   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
663   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
664   //    |   / |
665   //  7 +  +  + 6
666   //    | /9  |
667   //    |/    |
668   //  4 +--+--+ 3
669   //       8
670
671   const SMDS_MeshNode* N1 [6];
672   const SMDS_MeshNode* N2 [6];
673   if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
674     return false;
675   // now we receive following N1 and N2 (using numeration as above image)
676   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
677   // i.e. first nodes from both arrays determ new diagonal
678
679   const SMDS_MeshNode* N1new [6];
680   const SMDS_MeshNode* N2new [6];
681   N1new[0] = N1[0];
682   N1new[1] = N2[0];
683   N1new[2] = N2[1];
684   N1new[3] = N1[4];
685   N1new[4] = N2[3];
686   N1new[5] = N1[5];
687   N2new[0] = N1[0];
688   N2new[1] = N1[1];
689   N2new[2] = N2[0];
690   N2new[3] = N1[3];
691   N2new[4] = N2[5];
692   N2new[5] = N1[4];
693   // replaces nodes in faces
694   GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
695   GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
696
697   return true;
698 }
699
700 //=======================================================================
701 //function : findTriangles
702 //purpose  : find triangles sharing theNode1-theNode2 link
703 //=======================================================================
704
705 static bool findTriangles(const SMDS_MeshNode *    theNode1,
706                           const SMDS_MeshNode *    theNode2,
707                           const SMDS_MeshElement*& theTria1,
708                           const SMDS_MeshElement*& theTria2)
709 {
710   if ( !theNode1 || !theNode2 ) return false;
711
712   theTria1 = theTria2 = 0;
713
714   set< const SMDS_MeshElement* > emap;
715   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
716   while (it->more()) {
717     const SMDS_MeshElement* elem = it->next();
718     if ( elem->NbNodes() == 3 )
719       emap.insert( elem );
720   }
721   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
722   while (it->more()) {
723     const SMDS_MeshElement* elem = it->next();
724     if ( emap.find( elem ) != emap.end() ) {
725       if ( theTria1 ) {
726         // theTria1 must be element with minimum ID
727         if( theTria1->GetID() < elem->GetID() ) {
728           theTria2 = elem;
729         }
730         else {
731           theTria2 = theTria1;
732           theTria1 = elem;
733         }
734         break;
735       }
736       else {
737         theTria1 = elem;
738       }
739     }
740   }
741   return ( theTria1 && theTria2 );
742 }
743
744 //=======================================================================
745 //function : InverseDiag
746 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
747 //           with ones built on the same 4 nodes but having other common link.
748 //           Return false if proper faces not found
749 //=======================================================================
750
751 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
752                                     const SMDS_MeshNode * theNode2)
753 {
754   myLastCreatedElems.Clear();
755   myLastCreatedNodes.Clear();
756
757   MESSAGE( "::InverseDiag()" );
758
759   const SMDS_MeshElement *tr1, *tr2;
760   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
761     return false;
762
763   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
764   if (!F1) return false;
765   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
766   if (!F2) return false;
767   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
768       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
769
770     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
771     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
772     //    |/ |                                    | \|
773     //  B +--+ 2                                B +--+ 2
774
775     // put nodes in array
776     // and find indices of 1,2 and of A in tr1 and of B in tr2
777     int i, iA1 = 0, i1 = 0;
778     const SMDS_MeshNode* aNodes1 [3];
779     SMDS_ElemIteratorPtr it;
780     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
781       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
782       if ( aNodes1[ i ] == theNode1 )
783         iA1 = i; // node A in tr1
784       else if ( aNodes1[ i ] != theNode2 )
785         i1 = i;  // node 1
786     }
787     int iB2 = 0, i2 = 0;
788     const SMDS_MeshNode* aNodes2 [3];
789     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
790       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
791       if ( aNodes2[ i ] == theNode2 )
792         iB2 = i; // node B in tr2
793       else if ( aNodes2[ i ] != theNode1 )
794         i2 = i;  // node 2
795     }
796
797     // nodes 1 and 2 should not be the same
798     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
799       return false;
800
801     // tr1: A->2
802     aNodes1[ iA1 ] = aNodes2[ i2 ];
803     // tr2: B->1
804     aNodes2[ iB2 ] = aNodes1[ i1 ];
805
806     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
807     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
808
809     return true;
810   }
811
812   // check case of quadratic faces
813   return InverseDiag(tr1,tr2);
814 }
815
816 //=======================================================================
817 //function : getQuadrangleNodes
818 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
819 //           fusion of triangles tr1 and tr2 having shared link on
820 //           theNode1 and theNode2
821 //=======================================================================
822
823 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
824                         const SMDS_MeshNode *    theNode1,
825                         const SMDS_MeshNode *    theNode2,
826                         const SMDS_MeshElement * tr1,
827                         const SMDS_MeshElement * tr2 )
828 {
829   if( tr1->NbNodes() != tr2->NbNodes() )
830     return false;
831   // find the 4-th node to insert into tr1
832   const SMDS_MeshNode* n4 = 0;
833   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
834   int i=0;
835   while ( !n4 && i<3 ) {
836     const SMDS_MeshNode * n = cast2Node( it->next() );
837     i++;
838     bool isDiag = ( n == theNode1 || n == theNode2 );
839     if ( !isDiag )
840       n4 = n;
841   }
842   // Make an array of nodes to be in a quadrangle
843   int iNode = 0, iFirstDiag = -1;
844   it = tr1->nodesIterator();
845   i=0;
846   while ( i<3 ) {
847     const SMDS_MeshNode * n = cast2Node( it->next() );
848     i++;
849     bool isDiag = ( n == theNode1 || n == theNode2 );
850     if ( isDiag ) {
851       if ( iFirstDiag < 0 )
852         iFirstDiag = iNode;
853       else if ( iNode - iFirstDiag == 1 )
854         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
855     }
856     else if ( n == n4 ) {
857       return false; // tr1 and tr2 should not have all the same nodes
858     }
859     theQuadNodes[ iNode++ ] = n;
860   }
861   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
862     theQuadNodes[ iNode ] = n4;
863
864   return true;
865 }
866
867 //=======================================================================
868 //function : DeleteDiag
869 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
870 //           with a quadrangle built on the same 4 nodes.
871 //           Return false if proper faces not found
872 //=======================================================================
873
874 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
875                                    const SMDS_MeshNode * theNode2)
876 {
877   myLastCreatedElems.Clear();
878   myLastCreatedNodes.Clear();
879
880   MESSAGE( "::DeleteDiag()" );
881
882   const SMDS_MeshElement *tr1, *tr2;
883   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
884     return false;
885
886   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
887   if (!F1) return false;
888   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
889   if (!F2) return false;
890   SMESHDS_Mesh * aMesh = GetMeshDS();
891
892   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
893       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
894
895     const SMDS_MeshNode* aNodes [ 4 ];
896     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
897       return false;
898
899     const SMDS_MeshElement* newElem = 0;
900     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
901     myLastCreatedElems.Append(newElem);
902     AddToSameGroups( newElem, tr1, aMesh );
903     int aShapeId = tr1->getshapeId();
904     if ( aShapeId )
905       {
906         aMesh->SetMeshElementOnShape( newElem, aShapeId );
907       }
908     aMesh->RemoveElement( tr1 );
909     aMesh->RemoveElement( tr2 );
910
911     return true;
912   }
913
914   // check case of quadratic faces
915   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
916     return false;
917   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
918     return false;
919
920   //       5
921   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
922   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
923   //    |   / |
924   //  7 +  +  + 6
925   //    | /9  |
926   //    |/    |
927   //  4 +--+--+ 3
928   //       8
929
930   const SMDS_MeshNode* N1 [6];
931   const SMDS_MeshNode* N2 [6];
932   if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
933     return false;
934   // now we receive following N1 and N2 (using numeration as above image)
935   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
936   // i.e. first nodes from both arrays determ new diagonal
937
938   const SMDS_MeshNode* aNodes[8];
939   aNodes[0] = N1[0];
940   aNodes[1] = N1[1];
941   aNodes[2] = N2[0];
942   aNodes[3] = N2[1];
943   aNodes[4] = N1[3];
944   aNodes[5] = N2[5];
945   aNodes[6] = N2[3];
946   aNodes[7] = N1[5];
947
948   const SMDS_MeshElement* newElem = 0;
949   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
950                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
951   myLastCreatedElems.Append(newElem);
952   AddToSameGroups( newElem, tr1, aMesh );
953   int aShapeId = tr1->getshapeId();
954   if ( aShapeId )
955     {
956       aMesh->SetMeshElementOnShape( newElem, aShapeId );
957     }
958   aMesh->RemoveElement( tr1 );
959   aMesh->RemoveElement( tr2 );
960
961   // remove middle node (9)
962   GetMeshDS()->RemoveNode( N1[4] );
963
964   return true;
965 }
966
967 //=======================================================================
968 //function : Reorient
969 //purpose  : Reverse theElement orientation
970 //=======================================================================
971
972 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
973 {
974   MESSAGE("Reorient");
975   myLastCreatedElems.Clear();
976   myLastCreatedNodes.Clear();
977
978   if (!theElem)
979     return false;
980   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
981   if ( !it || !it->more() )
982     return false;
983
984   switch ( theElem->GetType() ) {
985
986   case SMDSAbs_Edge:
987   case SMDSAbs_Face: {
988     if(!theElem->IsQuadratic()) {
989       int i = theElem->NbNodes();
990       vector<const SMDS_MeshNode*> aNodes( i );
991       while ( it->more() )
992         aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
993       return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
994     }
995     else {
996       // quadratic elements
997       if(theElem->GetType()==SMDSAbs_Edge) {
998         vector<const SMDS_MeshNode*> aNodes(3);
999         aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1000         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1001         aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1002         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1003       }
1004       else {
1005         int nbn = theElem->NbNodes();
1006         vector<const SMDS_MeshNode*> aNodes(nbn);
1007         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1008         int i=1;
1009         for(; i<nbn/2; i++) {
1010           aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1011         }
1012         for(i=0; i<nbn/2; i++) {
1013           aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1014         }
1015         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1016       }
1017     }
1018   }
1019   case SMDSAbs_Volume: {
1020     if (theElem->IsPoly()) {
1021       // TODO reorient vtk polyhedron
1022       MESSAGE("reorient vtk polyhedron ?");
1023       const SMDS_VtkVolume* aPolyedre =
1024         dynamic_cast<const SMDS_VtkVolume*>( theElem );
1025       if (!aPolyedre) {
1026         MESSAGE("Warning: bad volumic element");
1027         return false;
1028       }
1029
1030       int nbFaces = aPolyedre->NbFaces();
1031       vector<const SMDS_MeshNode *> poly_nodes;
1032       vector<int> quantities (nbFaces);
1033
1034       // reverse each face of the polyedre
1035       for (int iface = 1; iface <= nbFaces; iface++) {
1036         int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1037         quantities[iface - 1] = nbFaceNodes;
1038
1039         for (inode = nbFaceNodes; inode >= 1; inode--) {
1040           const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1041           poly_nodes.push_back(curNode);
1042         }
1043       }
1044
1045       return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1046
1047     }
1048     else {
1049       SMDS_VolumeTool vTool;
1050       if ( !vTool.Set( theElem ))
1051         return false;
1052       vTool.Inverse();
1053       MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1054       return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1055     }
1056   }
1057   default:;
1058   }
1059
1060   return false;
1061 }
1062
1063 //================================================================================
1064 /*!
1065  * \brief Reorient faces.
1066  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1067  * \param theDirection - desired direction of normal of \a theFace
1068  * \param theFace - one of \a theFaces that sould be orientated according to
1069  *        \a theDirection and whose orientation defines orientation of other faces
1070  * \return number of reoriented faces.
1071  */
1072 //================================================================================
1073
1074 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1075                                   const gp_Dir&            theDirection,
1076                                   const SMDS_MeshElement * theFace)
1077 {
1078   int nbReori = 0;
1079   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1080
1081   if ( theFaces.empty() )
1082   {
1083     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1084     while ( fIt->more() )
1085       theFaces.insert( theFaces.end(), fIt->next() );
1086   }
1087
1088   // orient theFace according to theDirection
1089   gp_XYZ normal;
1090   SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1091   if ( normal * theDirection.XYZ() < 0 )
1092     nbReori += Reorient( theFace );
1093
1094   // Orient other faces
1095
1096   set< const SMDS_MeshElement* > startFaces;
1097   TIDSortedElemSet avoidSet;
1098   set< SMESH_TLink > checkedLinks;
1099   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1100
1101   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1102     theFaces.erase( theFace );
1103   startFaces.insert( theFace );
1104
1105   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1106   while ( startFace != startFaces.end() )
1107   {
1108     theFace = *startFace;
1109     const int nbNodes = theFace->NbCornerNodes();
1110
1111     avoidSet.clear();
1112     avoidSet.insert(theFace);
1113
1114     NLink link( theFace->GetNode( 0 ), 0 );
1115     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1116     {
1117       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1118       linkIt_isNew = checkedLinks.insert( link );
1119       if ( !linkIt_isNew.second )
1120       {
1121         // link has already been checked and won't be encountered more
1122         // if the group (theFaces) is manifold
1123         checkedLinks.erase( linkIt_isNew.first );
1124       }
1125       else
1126       {
1127         int nodeInd1, nodeInd2;
1128         const SMDS_MeshElement* otherFace = FindFaceInSet( link.first, link.second,
1129                                                            theFaces, avoidSet,
1130                                                            & nodeInd1, & nodeInd2);
1131         if ( otherFace && otherFace != theFace)
1132         {
1133           // link must be reversed in otherFace if orientation ot otherFace
1134           // is same as that of theFace
1135           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1136           {
1137             // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1138             //      << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1139             nbReori += Reorient( otherFace );
1140           }
1141           startFaces.insert( otherFace );
1142           if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1143             theFaces.erase( otherFace );
1144         }
1145       }
1146       std::swap( link.first, link.second );
1147     }
1148     startFaces.erase( startFace );
1149     startFace = startFaces.begin();
1150   }
1151   return nbReori;
1152 }
1153
1154 //=======================================================================
1155 //function : getBadRate
1156 //purpose  :
1157 //=======================================================================
1158
1159 static double getBadRate (const SMDS_MeshElement*               theElem,
1160                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1161 {
1162   SMESH::Controls::TSequenceOfXYZ P;
1163   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1164     return 1e100;
1165   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1166   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1167 }
1168
1169 //=======================================================================
1170 //function : QuadToTri
1171 //purpose  : Cut quadrangles into triangles.
1172 //           theCrit is used to select a diagonal to cut
1173 //=======================================================================
1174
1175 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1176                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1177 {
1178   myLastCreatedElems.Clear();
1179   myLastCreatedNodes.Clear();
1180
1181   MESSAGE( "::QuadToTri()" );
1182
1183   if ( !theCrit.get() )
1184     return false;
1185
1186   SMESHDS_Mesh * aMesh = GetMeshDS();
1187
1188   Handle(Geom_Surface) surface;
1189   SMESH_MesherHelper   helper( *GetMesh() );
1190
1191   TIDSortedElemSet::iterator itElem;
1192   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1193     const SMDS_MeshElement* elem = *itElem;
1194     if ( !elem || elem->GetType() != SMDSAbs_Face )
1195       continue;
1196     if ( elem->NbCornerNodes() != 4 )
1197       continue;
1198
1199     // retrieve element nodes
1200     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1201
1202     // compare two sets of possible triangles
1203     double aBadRate1, aBadRate2; // to what extent a set is bad
1204     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1205     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1206     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1207
1208     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1209     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1210     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1211
1212     int aShapeId = FindShape( elem );
1213     const SMDS_MeshElement* newElem1 = 0;
1214     const SMDS_MeshElement* newElem2 = 0;
1215
1216     if( !elem->IsQuadratic() ) {
1217
1218       // split liner quadrangle
1219
1220       if ( aBadRate1 <= aBadRate2 ) {
1221         // tr1 + tr2 is better
1222         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1223         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1224       }
1225       else {
1226         // tr3 + tr4 is better
1227         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1228         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1229       }
1230     }
1231     else {
1232
1233       // split quadratic quadrangle
1234
1235       // get surface elem is on
1236       if ( aShapeId != helper.GetSubShapeID() ) {
1237         surface.Nullify();
1238         TopoDS_Shape shape;
1239         if ( aShapeId > 0 )
1240           shape = aMesh->IndexToShape( aShapeId );
1241         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1242           TopoDS_Face face = TopoDS::Face( shape );
1243           surface = BRep_Tool::Surface( face );
1244           if ( !surface.IsNull() )
1245             helper.SetSubShape( shape );
1246         }
1247       }
1248       // find middle point for (0,1,2,3)
1249       // and create a node in this point;
1250       const SMDS_MeshNode* newN = 0;
1251       if ( aNodes.size() == 9 )
1252       {
1253         // SMDSEntity_BiQuad_Quadrangle
1254         newN = aNodes.back();
1255       }
1256       else
1257       {
1258         gp_XYZ p( 0,0,0 );
1259         if ( surface.IsNull() )
1260         {
1261           for ( int i = 0; i < 4; i++ )
1262             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1263           p /= 4;
1264         }
1265         else
1266         {
1267           const SMDS_MeshNode* inFaceNode = 0;
1268           if ( helper.GetNodeUVneedInFaceNode() )
1269             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1270               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1271                 inFaceNode = aNodes[ i ];
1272
1273           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1274           gp_XY uv( 0,0 );
1275           for ( int i = 0; i < 4; i++ )
1276             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1277           uv /= 4.;
1278           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1279         }
1280         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1281         myLastCreatedNodes.Append(newN);
1282       }
1283       // create a new element
1284       if ( aBadRate1 <= aBadRate2 ) {
1285         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1286                                   aNodes[6], aNodes[7], newN );
1287         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1288                                   newN,      aNodes[4], aNodes[5] );
1289       }
1290       else {
1291         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1292                                   aNodes[7], aNodes[4], newN );
1293         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1294                                   newN,      aNodes[5], aNodes[6] );
1295       }
1296     } // quadratic case
1297
1298     // care of a new element
1299
1300     myLastCreatedElems.Append(newElem1);
1301     myLastCreatedElems.Append(newElem2);
1302     AddToSameGroups( newElem1, elem, aMesh );
1303     AddToSameGroups( newElem2, elem, aMesh );
1304
1305     // put a new triangle on the same shape
1306     if ( aShapeId )
1307       {
1308         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1309         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1310       }
1311     aMesh->RemoveElement( elem );
1312   }
1313   return true;
1314 }
1315
1316 //=======================================================================
1317 //function : BestSplit
1318 //purpose  : Find better diagonal for cutting.
1319 //=======================================================================
1320
1321 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1322                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1323 {
1324   myLastCreatedElems.Clear();
1325   myLastCreatedNodes.Clear();
1326
1327   if (!theCrit.get())
1328     return -1;
1329
1330   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1331     return -1;
1332
1333   if( theQuad->NbNodes()==4 ||
1334       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1335
1336     // retrieve element nodes
1337     const SMDS_MeshNode* aNodes [4];
1338     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1339     int i = 0;
1340     //while (itN->more())
1341     while (i<4) {
1342       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1343     }
1344     // compare two sets of possible triangles
1345     double aBadRate1, aBadRate2; // to what extent a set is bad
1346     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1347     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1348     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1349
1350     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1351     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1352     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1353
1354     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1355       return 1; // diagonal 1-3
1356
1357     return 2; // diagonal 2-4
1358   }
1359   return -1;
1360 }
1361
1362 namespace
1363 {
1364   // Methods of splitting volumes into tetra
1365
1366   const int theHexTo5_1[5*4+1] =
1367     {
1368       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1369     };
1370   const int theHexTo5_2[5*4+1] =
1371     {
1372       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1373     };
1374   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1375
1376   const int theHexTo6_1[6*4+1] =
1377     {
1378       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
1379     };
1380   const int theHexTo6_2[6*4+1] =
1381     {
1382       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
1383     };
1384   const int theHexTo6_3[6*4+1] =
1385     {
1386       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
1387     };
1388   const int theHexTo6_4[6*4+1] =
1389     {
1390       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
1391     };
1392   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1393
1394   const int thePyraTo2_1[2*4+1] =
1395     {
1396       0, 1, 2, 4,    0, 2, 3, 4,   -1
1397     };
1398   const int thePyraTo2_2[2*4+1] =
1399     {
1400       1, 2, 3, 4,    1, 3, 0, 4,   -1
1401     };
1402   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1403
1404   const int thePentaTo3_1[3*4+1] =
1405     {
1406       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1407     };
1408   const int thePentaTo3_2[3*4+1] =
1409     {
1410       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1411     };
1412   const int thePentaTo3_3[3*4+1] =
1413     {
1414       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1415     };
1416   const int thePentaTo3_4[3*4+1] =
1417     {
1418       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1419     };
1420   const int thePentaTo3_5[3*4+1] =
1421     {
1422       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1423     };
1424   const int thePentaTo3_6[3*4+1] =
1425     {
1426       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1427     };
1428   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1429                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1430
1431   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1432   {
1433     int _n1, _n2, _n3;
1434     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1435     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1436     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1437   };
1438   struct TSplitMethod
1439   {
1440     int        _nbTetra;
1441     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1442     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1443     bool       _ownConn;      //!< to delete _connectivity in destructor
1444     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1445
1446     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1447       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1448     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1449     bool hasFacet( const TTriangleFacet& facet ) const
1450     {
1451       const int* tetConn = _connectivity;
1452       for ( ; tetConn[0] >= 0; tetConn += 4 )
1453         if (( facet.contains( tetConn[0] ) +
1454               facet.contains( tetConn[1] ) +
1455               facet.contains( tetConn[2] ) +
1456               facet.contains( tetConn[3] )) == 3 )
1457           return true;
1458       return false;
1459     }
1460   };
1461
1462   //=======================================================================
1463   /*!
1464    * \brief return TSplitMethod for the given element
1465    */
1466   //=======================================================================
1467
1468   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1469   {
1470     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1471
1472     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1473     // an edge and a face barycenter; tertaherdons are based on triangles and
1474     // a volume barycenter
1475     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1476
1477     // Find out how adjacent volumes are split
1478
1479     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1480     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1481     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1482     {
1483       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1484       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1485       if ( nbNodes < 4 ) continue;
1486
1487       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1488       const int* nInd = vol.GetFaceNodesIndices( iF );
1489       if ( nbNodes == 4 )
1490       {
1491         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1492         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1493         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1494         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1495       }
1496       else
1497       {
1498         int iCom = 0; // common node of triangle faces to split into
1499         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1500         {
1501           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1502                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1503                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1504           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1505                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1506                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1507           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1508           {
1509             triaSplits.push_back( t012 );
1510             triaSplits.push_back( t023 );
1511             break;
1512           }
1513         }
1514       }
1515       if ( !triaSplits.empty() )
1516         hasAdjacentSplits = true;
1517     }
1518
1519     // Among variants of split method select one compliant with adjacent volumes
1520
1521     TSplitMethod method;
1522     if ( !vol.Element()->IsPoly() && !is24TetMode )
1523     {
1524       int nbVariants = 2, nbTet = 0;
1525       const int** connVariants = 0;
1526       switch ( vol.Element()->GetEntityType() )
1527       {
1528       case SMDSEntity_Hexa:
1529       case SMDSEntity_Quad_Hexa:
1530       case SMDSEntity_TriQuad_Hexa:
1531         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1532           connVariants = theHexTo5, nbTet = 5;
1533         else
1534           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1535         break;
1536       case SMDSEntity_Pyramid:
1537       case SMDSEntity_Quad_Pyramid:
1538         connVariants = thePyraTo2;  nbTet = 2;
1539         break;
1540       case SMDSEntity_Penta:
1541       case SMDSEntity_Quad_Penta:
1542         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1543         break;
1544       default:
1545         nbVariants = 0;
1546       }
1547       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1548       {
1549         // check method compliancy with adjacent tetras,
1550         // all found splits must be among facets of tetras described by this method
1551         method = TSplitMethod( nbTet, connVariants[variant] );
1552         if ( hasAdjacentSplits && method._nbTetra > 0 )
1553         {
1554           bool facetCreated = true;
1555           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1556           {
1557             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1558             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1559               facetCreated = method.hasFacet( *facet );
1560           }
1561           if ( !facetCreated )
1562             method = TSplitMethod(0); // incompatible method
1563         }
1564       }
1565     }
1566     if ( method._nbTetra < 1 )
1567     {
1568       // No standard method is applicable, use a generic solution:
1569       // each facet of a volume is split into triangles and
1570       // each of triangles and a volume barycenter form a tetrahedron.
1571
1572       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1573
1574       int* connectivity = new int[ maxTetConnSize + 1 ];
1575       method._connectivity = connectivity;
1576       method._ownConn = true;
1577       method._baryNode = !isHex27; // to create central node or not
1578
1579       int connSize = 0;
1580       int baryCenInd = vol.NbNodes() - int( isHex27 );
1581       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1582       {
1583         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1584         const int*   nInd = vol.GetFaceNodesIndices( iF );
1585         // find common node of triangle facets of tetra to create
1586         int iCommon = 0; // index in linear numeration
1587         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1588         if ( !triaSplits.empty() )
1589         {
1590           // by found facets
1591           const TTriangleFacet* facet = &triaSplits.front();
1592           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1593             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1594                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1595               break;
1596         }
1597         else if ( nbNodes > 3 && !is24TetMode )
1598         {
1599           // find the best method of splitting into triangles by aspect ratio
1600           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1601           map< double, int > badness2iCommon;
1602           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1603           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1604           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1605           {
1606             double badness = 0;
1607             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1608             {
1609               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1610                                       nodes[ iQ*((iLast-1)%nbNodes)],
1611                                       nodes[ iQ*((iLast  )%nbNodes)]);
1612               badness += getBadRate( &tria, aspectRatio );
1613             }
1614             badness2iCommon.insert( make_pair( badness, iCommon ));
1615           }
1616           // use iCommon with lowest badness
1617           iCommon = badness2iCommon.begin()->second;
1618         }
1619         if ( iCommon >= nbNodes )
1620           iCommon = 0; // something wrong
1621
1622         // fill connectivity of tetrahedra based on a current face
1623         int nbTet = nbNodes - 2;
1624         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1625         {
1626           int faceBaryCenInd;
1627           if ( isHex27 )
1628           {
1629             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1630             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1631           }
1632           else
1633           {
1634             method._faceBaryNode[ iF ] = 0;
1635             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1636           }
1637           nbTet = nbNodes;
1638           for ( int i = 0; i < nbTet; ++i )
1639           {
1640             int i1 = i, i2 = (i+1) % nbNodes;
1641             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1642             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1643             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1644             connectivity[ connSize++ ] = faceBaryCenInd;
1645             connectivity[ connSize++ ] = baryCenInd;
1646           }
1647         }
1648         else
1649         {
1650           for ( int i = 0; i < nbTet; ++i )
1651           {
1652             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1653             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1654             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1655             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1656             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1657             connectivity[ connSize++ ] = baryCenInd;
1658           }
1659         }
1660         method._nbTetra += nbTet;
1661
1662       } // loop on volume faces
1663
1664       connectivity[ connSize++ ] = -1;
1665
1666     } // end of generic solution
1667
1668     return method;
1669   }
1670   //================================================================================
1671   /*!
1672    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1673    */
1674   //================================================================================
1675
1676   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1677   {
1678     // find the tetrahedron including the three nodes of facet
1679     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1680     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1681     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1682     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1683     while ( volIt1->more() )
1684     {
1685       const SMDS_MeshElement* v = volIt1->next();
1686       SMDSAbs_EntityType type = v->GetEntityType();
1687       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1688         continue;
1689       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1690         continue; // medium node not allowed
1691       const int ind2 = v->GetNodeIndex( n2 );
1692       if ( ind2 < 0 || 3 < ind2 )
1693         continue;
1694       const int ind3 = v->GetNodeIndex( n3 );
1695       if ( ind3 < 0 || 3 < ind3 )
1696         continue;
1697       return true;
1698     }
1699     return false;
1700   }
1701
1702   //=======================================================================
1703   /*!
1704    * \brief A key of a face of volume
1705    */
1706   //=======================================================================
1707
1708   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1709   {
1710     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1711     {
1712       TIDSortedNodeSet sortedNodes;
1713       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1714       int nbNodes = vol.NbFaceNodes( iF );
1715       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1716       for ( int i = 0; i < nbNodes; i += iQ )
1717         sortedNodes.insert( fNodes[i] );
1718       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1719       first.first   = (*(n++))->GetID();
1720       first.second  = (*(n++))->GetID();
1721       second.first  = (*(n++))->GetID();
1722       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1723     }
1724   };
1725 } // namespace
1726
1727 //=======================================================================
1728 //function : SplitVolumesIntoTetra
1729 //purpose  : Split volume elements into tetrahedra.
1730 //=======================================================================
1731
1732 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1733                                               const int                theMethodFlags)
1734 {
1735   // std-like iterator on coordinates of nodes of mesh element
1736   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1737   NXyzIterator xyzEnd;
1738
1739   SMDS_VolumeTool    volTool;
1740   SMESH_MesherHelper helper( *GetMesh());
1741
1742   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1743   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1744   
1745   SMESH_SequenceOfElemPtr newNodes, newElems;
1746
1747   // map face of volume to it's baricenrtic node
1748   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1749   double bc[3];
1750
1751   TIDSortedElemSet::const_iterator elem = theElems.begin();
1752   for ( ; elem != theElems.end(); ++elem )
1753   {
1754     if ( (*elem)->GetType() != SMDSAbs_Volume )
1755       continue;
1756     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1757     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1758       continue;
1759
1760     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1761
1762     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1763     if ( splitMethod._nbTetra < 1 ) continue;
1764
1765     // find submesh to add new tetras to
1766     if ( !subMesh || !subMesh->Contains( *elem ))
1767     {
1768       int shapeID = FindShape( *elem );
1769       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1770       subMesh = GetMeshDS()->MeshElements( shapeID );
1771     }
1772     int iQ;
1773     if ( (*elem)->IsQuadratic() )
1774     {
1775       iQ = 2;
1776       // add quadratic links to the helper
1777       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1778       {
1779         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1780         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1781         for ( int iN = 0; iN < nbN; iN += iQ )
1782           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1783       }
1784       helper.SetIsQuadratic( true );
1785     }
1786     else
1787     {
1788       iQ = 1;
1789       helper.SetIsQuadratic( false );
1790     }
1791     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1792     helper.SetElementsOnShape( true );
1793     if ( splitMethod._baryNode )
1794     {
1795       // make a node at barycenter
1796       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1797       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1798       nodes.push_back( gcNode );
1799       newNodes.Append( gcNode );
1800     }
1801     if ( !splitMethod._faceBaryNode.empty() )
1802     {
1803       // make or find baricentric nodes of faces
1804       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1805       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1806       {
1807         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1808           volFace2BaryNode.insert
1809           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1810         if ( !f_n->second )
1811         {
1812           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1813           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1814         }
1815         nodes.push_back( iF_n->second = f_n->second );
1816       }
1817     }
1818
1819     // make tetras
1820     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1821     const int* tetConn = splitMethod._connectivity;
1822     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1823       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1824                                                        nodes[ tetConn[1] ],
1825                                                        nodes[ tetConn[2] ],
1826                                                        nodes[ tetConn[3] ]));
1827
1828     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1829
1830     // Split faces on sides of the split volume
1831
1832     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1833     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1834     {
1835       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1836       if ( nbNodes < 4 ) continue;
1837
1838       // find an existing face
1839       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1840                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1841       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1842                                                                        /*noMedium=*/false))
1843       {
1844         // make triangles
1845         helper.SetElementsOnShape( false );
1846         vector< const SMDS_MeshElement* > triangles;
1847
1848         // find submesh to add new triangles in
1849         if ( !fSubMesh || !fSubMesh->Contains( face ))
1850         {
1851           int shapeID = FindShape( face );
1852           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1853         }
1854         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1855         if ( iF_n != splitMethod._faceBaryNode.end() )
1856         {
1857           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1858           {
1859             const SMDS_MeshNode* n1 = fNodes[iN];
1860             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1861             const SMDS_MeshNode *n3 = iF_n->second;
1862             if ( !volTool.IsFaceExternal( iF ))
1863               swap( n2, n3 );
1864             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1865
1866             if ( fSubMesh && n3->getshapeId() < 1 )
1867               fSubMesh->AddNode( n3 );
1868           }
1869         }
1870         else
1871         {
1872           // among possible triangles create ones discribed by split method
1873           const int* nInd = volTool.GetFaceNodesIndices( iF );
1874           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1875           int iCom = 0; // common node of triangle faces to split into
1876           list< TTriangleFacet > facets;
1877           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1878           {
1879             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1880                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1881                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1882             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1883                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1884                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1885             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1886             {
1887               facets.push_back( t012 );
1888               facets.push_back( t023 );
1889               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1890                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1891                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1892                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1893               break;
1894             }
1895           }
1896           list< TTriangleFacet >::iterator facet = facets.begin();
1897           for ( ; facet != facets.end(); ++facet )
1898           {
1899             if ( !volTool.IsFaceExternal( iF ))
1900               swap( facet->_n2, facet->_n3 );
1901             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1902                                                  volNodes[ facet->_n2 ],
1903                                                  volNodes[ facet->_n3 ]));
1904           }
1905         }
1906         for ( int i = 0; i < triangles.size(); ++i )
1907         {
1908           if ( !triangles[i] ) continue;
1909           if ( fSubMesh )
1910             fSubMesh->AddElement( triangles[i]);
1911           newElems.Append( triangles[i] );
1912         }
1913         ReplaceElemInGroups( face, triangles, GetMeshDS() );
1914         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1915       }
1916
1917     } // loop on volume faces to split them into triangles
1918
1919     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1920
1921     if ( geomType == SMDSEntity_TriQuad_Hexa )
1922     {
1923       // remove medium nodes that could become free
1924       for ( int i = 20; i < volTool.NbNodes(); ++i )
1925         if ( volNodes[i]->NbInverseElements() == 0 )
1926           GetMeshDS()->RemoveNode( volNodes[i] );
1927     }
1928   } // loop on volumes to split
1929
1930   myLastCreatedNodes = newNodes;
1931   myLastCreatedElems = newElems;
1932 }
1933
1934 //=======================================================================
1935 //function : AddToSameGroups
1936 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
1937 //=======================================================================
1938
1939 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1940                                         const SMDS_MeshElement* elemInGroups,
1941                                         SMESHDS_Mesh *          aMesh)
1942 {
1943   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1944   if (!groups.empty()) {
1945     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1946     for ( ; grIt != groups.end(); grIt++ ) {
1947       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1948       if ( group && group->Contains( elemInGroups ))
1949         group->SMDSGroup().Add( elemToAdd );
1950     }
1951   }
1952 }
1953
1954
1955 //=======================================================================
1956 //function : RemoveElemFromGroups
1957 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
1958 //=======================================================================
1959 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1960                                              SMESHDS_Mesh *          aMesh)
1961 {
1962   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1963   if (!groups.empty())
1964   {
1965     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1966     for (; GrIt != groups.end(); GrIt++)
1967     {
1968       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1969       if (!grp || grp->IsEmpty()) continue;
1970       grp->SMDSGroup().Remove(removeelem);
1971     }
1972   }
1973 }
1974
1975 //================================================================================
1976 /*!
1977  * \brief Replace elemToRm by elemToAdd in the all groups
1978  */
1979 //================================================================================
1980
1981 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1982                                             const SMDS_MeshElement* elemToAdd,
1983                                             SMESHDS_Mesh *          aMesh)
1984 {
1985   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1986   if (!groups.empty()) {
1987     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1988     for ( ; grIt != groups.end(); grIt++ ) {
1989       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1990       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1991         group->SMDSGroup().Add( elemToAdd );
1992     }
1993   }
1994 }
1995
1996 //================================================================================
1997 /*!
1998  * \brief Replace elemToRm by elemToAdd in the all groups
1999  */
2000 //================================================================================
2001
2002 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2003                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2004                                             SMESHDS_Mesh *                         aMesh)
2005 {
2006   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2007   if (!groups.empty())
2008   {
2009     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2010     for ( ; grIt != groups.end(); grIt++ ) {
2011       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2012       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2013         for ( int i = 0; i < elemToAdd.size(); ++i )
2014           group->SMDSGroup().Add( elemToAdd[ i ] );
2015     }
2016   }
2017 }
2018
2019 //=======================================================================
2020 //function : QuadToTri
2021 //purpose  : Cut quadrangles into triangles.
2022 //           theCrit is used to select a diagonal to cut
2023 //=======================================================================
2024
2025 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2026                                   const bool         the13Diag)
2027 {
2028   myLastCreatedElems.Clear();
2029   myLastCreatedNodes.Clear();
2030
2031   MESSAGE( "::QuadToTri()" );
2032
2033   SMESHDS_Mesh * aMesh = GetMeshDS();
2034
2035   Handle(Geom_Surface) surface;
2036   SMESH_MesherHelper   helper( *GetMesh() );
2037
2038   TIDSortedElemSet::iterator itElem;
2039   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2040     const SMDS_MeshElement* elem = *itElem;
2041     if ( !elem || elem->GetType() != SMDSAbs_Face )
2042       continue;
2043     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2044     if(!isquad) continue;
2045
2046     if(elem->NbNodes()==4) {
2047       // retrieve element nodes
2048       const SMDS_MeshNode* aNodes [4];
2049       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2050       int i = 0;
2051       while ( itN->more() )
2052         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2053
2054       int aShapeId = FindShape( elem );
2055       const SMDS_MeshElement* newElem1 = 0;
2056       const SMDS_MeshElement* newElem2 = 0;
2057       if ( the13Diag ) {
2058         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2059         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2060       }
2061       else {
2062         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2063         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2064       }
2065       myLastCreatedElems.Append(newElem1);
2066       myLastCreatedElems.Append(newElem2);
2067       // put a new triangle on the same shape and add to the same groups
2068       if ( aShapeId )
2069         {
2070           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2071           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2072         }
2073       AddToSameGroups( newElem1, elem, aMesh );
2074       AddToSameGroups( newElem2, elem, aMesh );
2075       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2076       aMesh->RemoveElement( elem );
2077     }
2078
2079     // Quadratic quadrangle
2080
2081     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2082
2083       // get surface elem is on
2084       int aShapeId = FindShape( elem );
2085       if ( aShapeId != helper.GetSubShapeID() ) {
2086         surface.Nullify();
2087         TopoDS_Shape shape;
2088         if ( aShapeId > 0 )
2089           shape = aMesh->IndexToShape( aShapeId );
2090         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2091           TopoDS_Face face = TopoDS::Face( shape );
2092           surface = BRep_Tool::Surface( face );
2093           if ( !surface.IsNull() )
2094             helper.SetSubShape( shape );
2095         }
2096       }
2097
2098       const SMDS_MeshNode* aNodes [8];
2099       const SMDS_MeshNode* inFaceNode = 0;
2100       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2101       int i = 0;
2102       while ( itN->more() ) {
2103         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2104         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2105              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2106         {
2107           inFaceNode = aNodes[ i-1 ];
2108         }
2109       }
2110
2111       // find middle point for (0,1,2,3)
2112       // and create a node in this point;
2113       gp_XYZ p( 0,0,0 );
2114       if ( surface.IsNull() ) {
2115         for(i=0; i<4; i++)
2116           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2117         p /= 4;
2118       }
2119       else {
2120         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2121         gp_XY uv( 0,0 );
2122         for(i=0; i<4; i++)
2123           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2124         uv /= 4.;
2125         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2126       }
2127       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2128       myLastCreatedNodes.Append(newN);
2129
2130       // create a new element
2131       const SMDS_MeshElement* newElem1 = 0;
2132       const SMDS_MeshElement* newElem2 = 0;
2133       if ( the13Diag ) {
2134         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2135                                   aNodes[6], aNodes[7], newN );
2136         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2137                                   newN,      aNodes[4], aNodes[5] );
2138       }
2139       else {
2140         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2141                                   aNodes[7], aNodes[4], newN );
2142         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2143                                   newN,      aNodes[5], aNodes[6] );
2144       }
2145       myLastCreatedElems.Append(newElem1);
2146       myLastCreatedElems.Append(newElem2);
2147       // put a new triangle on the same shape and add to the same groups
2148       if ( aShapeId )
2149         {
2150           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2151           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2152         }
2153       AddToSameGroups( newElem1, elem, aMesh );
2154       AddToSameGroups( newElem2, elem, aMesh );
2155       aMesh->RemoveElement( elem );
2156     }
2157   }
2158
2159   return true;
2160 }
2161
2162 //=======================================================================
2163 //function : getAngle
2164 //purpose  :
2165 //=======================================================================
2166
2167 double getAngle(const SMDS_MeshElement * tr1,
2168                 const SMDS_MeshElement * tr2,
2169                 const SMDS_MeshNode *    n1,
2170                 const SMDS_MeshNode *    n2)
2171 {
2172   double angle = 2. * M_PI; // bad angle
2173
2174   // get normals
2175   SMESH::Controls::TSequenceOfXYZ P1, P2;
2176   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2177        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2178     return angle;
2179   gp_Vec N1,N2;
2180   if(!tr1->IsQuadratic())
2181     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2182   else
2183     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2184   if ( N1.SquareMagnitude() <= gp::Resolution() )
2185     return angle;
2186   if(!tr2->IsQuadratic())
2187     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2188   else
2189     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2190   if ( N2.SquareMagnitude() <= gp::Resolution() )
2191     return angle;
2192
2193   // find the first diagonal node n1 in the triangles:
2194   // take in account a diagonal link orientation
2195   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2196   for ( int t = 0; t < 2; t++ ) {
2197     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2198     int i = 0, iDiag = -1;
2199     while ( it->more()) {
2200       const SMDS_MeshElement *n = it->next();
2201       if ( n == n1 || n == n2 ) {
2202         if ( iDiag < 0)
2203           iDiag = i;
2204         else {
2205           if ( i - iDiag == 1 )
2206             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2207           else
2208             nFirst[ t ] = n;
2209           break;
2210         }
2211       }
2212       i++;
2213     }
2214   }
2215   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2216     N2.Reverse();
2217
2218   angle = N1.Angle( N2 );
2219   //SCRUTE( angle );
2220   return angle;
2221 }
2222
2223 // =================================================
2224 // class generating a unique ID for a pair of nodes
2225 // and able to return nodes by that ID
2226 // =================================================
2227 class LinkID_Gen {
2228 public:
2229
2230   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2231     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2232   {}
2233
2234   long GetLinkID (const SMDS_MeshNode * n1,
2235                   const SMDS_MeshNode * n2) const
2236   {
2237     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2238   }
2239
2240   bool GetNodes (const long             theLinkID,
2241                  const SMDS_MeshNode* & theNode1,
2242                  const SMDS_MeshNode* & theNode2) const
2243   {
2244     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2245     if ( !theNode1 ) return false;
2246     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2247     if ( !theNode2 ) return false;
2248     return true;
2249   }
2250
2251 private:
2252   LinkID_Gen();
2253   const SMESHDS_Mesh* myMesh;
2254   long                myMaxID;
2255 };
2256
2257
2258 //=======================================================================
2259 //function : TriToQuad
2260 //purpose  : Fuse neighbour triangles into quadrangles.
2261 //           theCrit is used to select a neighbour to fuse with.
2262 //           theMaxAngle is a max angle between element normals at which
2263 //           fusion is still performed.
2264 //=======================================================================
2265
2266 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2267                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2268                                   const double                         theMaxAngle)
2269 {
2270   myLastCreatedElems.Clear();
2271   myLastCreatedNodes.Clear();
2272
2273   MESSAGE( "::TriToQuad()" );
2274
2275   if ( !theCrit.get() )
2276     return false;
2277
2278   SMESHDS_Mesh * aMesh = GetMeshDS();
2279
2280   // Prepare data for algo: build
2281   // 1. map of elements with their linkIDs
2282   // 2. map of linkIDs with their elements
2283
2284   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2285   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2286   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2287   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2288
2289   TIDSortedElemSet::iterator itElem;
2290   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2291     const SMDS_MeshElement* elem = *itElem;
2292     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2293     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2294     if(!IsTria) continue;
2295
2296     // retrieve element nodes
2297     const SMDS_MeshNode* aNodes [4];
2298     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2299     int i = 0;
2300     while ( i<3 )
2301       aNodes[ i++ ] = cast2Node( itN->next() );
2302     aNodes[ 3 ] = aNodes[ 0 ];
2303
2304     // fill maps
2305     for ( i = 0; i < 3; i++ ) {
2306       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2307       // check if elements sharing a link can be fused
2308       itLE = mapLi_listEl.find( link );
2309       if ( itLE != mapLi_listEl.end() ) {
2310         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2311           continue;
2312         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2313         //if ( FindShape( elem ) != FindShape( elem2 ))
2314         //  continue; // do not fuse triangles laying on different shapes
2315         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2316           continue; // avoid making badly shaped quads
2317         (*itLE).second.push_back( elem );
2318       }
2319       else {
2320         mapLi_listEl[ link ].push_back( elem );
2321       }
2322       mapEl_setLi [ elem ].insert( link );
2323     }
2324   }
2325   // Clean the maps from the links shared by a sole element, ie
2326   // links to which only one element is bound in mapLi_listEl
2327
2328   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2329     int nbElems = (*itLE).second.size();
2330     if ( nbElems < 2  ) {
2331       const SMDS_MeshElement* elem = (*itLE).second.front();
2332       SMESH_TLink link = (*itLE).first;
2333       mapEl_setLi[ elem ].erase( link );
2334       if ( mapEl_setLi[ elem ].empty() )
2335         mapEl_setLi.erase( elem );
2336     }
2337   }
2338
2339   // Algo: fuse triangles into quadrangles
2340
2341   while ( ! mapEl_setLi.empty() ) {
2342     // Look for the start element:
2343     // the element having the least nb of shared links
2344     const SMDS_MeshElement* startElem = 0;
2345     int minNbLinks = 4;
2346     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2347       int nbLinks = (*itEL).second.size();
2348       if ( nbLinks < minNbLinks ) {
2349         startElem = (*itEL).first;
2350         minNbLinks = nbLinks;
2351         if ( minNbLinks == 1 )
2352           break;
2353       }
2354     }
2355
2356     // search elements to fuse starting from startElem or links of elements
2357     // fused earlyer - startLinks
2358     list< SMESH_TLink > startLinks;
2359     while ( startElem || !startLinks.empty() ) {
2360       while ( !startElem && !startLinks.empty() ) {
2361         // Get an element to start, by a link
2362         SMESH_TLink linkId = startLinks.front();
2363         startLinks.pop_front();
2364         itLE = mapLi_listEl.find( linkId );
2365         if ( itLE != mapLi_listEl.end() ) {
2366           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2367           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2368           for ( ; itE != listElem.end() ; itE++ )
2369             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2370               startElem = (*itE);
2371           mapLi_listEl.erase( itLE );
2372         }
2373       }
2374
2375       if ( startElem ) {
2376         // Get candidates to be fused
2377         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2378         const SMESH_TLink *link12, *link13;
2379         startElem = 0;
2380         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2381         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2382         ASSERT( !setLi.empty() );
2383         set< SMESH_TLink >::iterator itLi;
2384         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2385         {
2386           const SMESH_TLink & link = (*itLi);
2387           itLE = mapLi_listEl.find( link );
2388           if ( itLE == mapLi_listEl.end() )
2389             continue;
2390
2391           const SMDS_MeshElement* elem = (*itLE).second.front();
2392           if ( elem == tr1 )
2393             elem = (*itLE).second.back();
2394           mapLi_listEl.erase( itLE );
2395           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2396             continue;
2397           if ( tr2 ) {
2398             tr3 = elem;
2399             link13 = &link;
2400           }
2401           else {
2402             tr2 = elem;
2403             link12 = &link;
2404           }
2405
2406           // add other links of elem to list of links to re-start from
2407           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2408           set< SMESH_TLink >::iterator it;
2409           for ( it = links.begin(); it != links.end(); it++ ) {
2410             const SMESH_TLink& link2 = (*it);
2411             if ( link2 != link )
2412               startLinks.push_back( link2 );
2413           }
2414         }
2415
2416         // Get nodes of possible quadrangles
2417         const SMDS_MeshNode *n12 [4], *n13 [4];
2418         bool Ok12 = false, Ok13 = false;
2419         const SMDS_MeshNode *linkNode1, *linkNode2;
2420         if(tr2) {
2421           linkNode1 = link12->first;
2422           linkNode2 = link12->second;
2423           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2424             Ok12 = true;
2425         }
2426         if(tr3) {
2427           linkNode1 = link13->first;
2428           linkNode2 = link13->second;
2429           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2430             Ok13 = true;
2431         }
2432
2433         // Choose a pair to fuse
2434         if ( Ok12 && Ok13 ) {
2435           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2436           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2437           double aBadRate12 = getBadRate( &quad12, theCrit );
2438           double aBadRate13 = getBadRate( &quad13, theCrit );
2439           if (  aBadRate13 < aBadRate12 )
2440             Ok12 = false;
2441           else
2442             Ok13 = false;
2443         }
2444
2445         // Make quadrangles
2446         // and remove fused elems and removed links from the maps
2447         mapEl_setLi.erase( tr1 );
2448         if ( Ok12 ) {
2449           mapEl_setLi.erase( tr2 );
2450           mapLi_listEl.erase( *link12 );
2451           if(tr1->NbNodes()==3) {
2452             const SMDS_MeshElement* newElem = 0;
2453             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2454             myLastCreatedElems.Append(newElem);
2455             AddToSameGroups( newElem, tr1, aMesh );
2456             int aShapeId = tr1->getshapeId();
2457             if ( aShapeId )
2458               {
2459                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2460               }
2461             aMesh->RemoveElement( tr1 );
2462             aMesh->RemoveElement( tr2 );
2463           }
2464           else {
2465             const SMDS_MeshNode* N1 [6];
2466             const SMDS_MeshNode* N2 [6];
2467             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2468             // now we receive following N1 and N2 (using numeration as above image)
2469             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2470             // i.e. first nodes from both arrays determ new diagonal
2471             const SMDS_MeshNode* aNodes[8];
2472             aNodes[0] = N1[0];
2473             aNodes[1] = N1[1];
2474             aNodes[2] = N2[0];
2475             aNodes[3] = N2[1];
2476             aNodes[4] = N1[3];
2477             aNodes[5] = N2[5];
2478             aNodes[6] = N2[3];
2479             aNodes[7] = N1[5];
2480             const SMDS_MeshElement* newElem = 0;
2481             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2482                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2483             myLastCreatedElems.Append(newElem);
2484             AddToSameGroups( newElem, tr1, aMesh );
2485             int aShapeId = tr1->getshapeId();
2486             if ( aShapeId )
2487               {
2488                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2489               }
2490             aMesh->RemoveElement( tr1 );
2491             aMesh->RemoveElement( tr2 );
2492             // remove middle node (9)
2493             GetMeshDS()->RemoveNode( N1[4] );
2494           }
2495         }
2496         else if ( Ok13 ) {
2497           mapEl_setLi.erase( tr3 );
2498           mapLi_listEl.erase( *link13 );
2499           if(tr1->NbNodes()==3) {
2500             const SMDS_MeshElement* newElem = 0;
2501             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2502             myLastCreatedElems.Append(newElem);
2503             AddToSameGroups( newElem, tr1, aMesh );
2504             int aShapeId = tr1->getshapeId();
2505             if ( aShapeId )
2506               {
2507                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2508               }
2509             aMesh->RemoveElement( tr1 );
2510             aMesh->RemoveElement( tr3 );
2511           }
2512           else {
2513             const SMDS_MeshNode* N1 [6];
2514             const SMDS_MeshNode* N2 [6];
2515             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2516             // now we receive following N1 and N2 (using numeration as above image)
2517             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2518             // i.e. first nodes from both arrays determ new diagonal
2519             const SMDS_MeshNode* aNodes[8];
2520             aNodes[0] = N1[0];
2521             aNodes[1] = N1[1];
2522             aNodes[2] = N2[0];
2523             aNodes[3] = N2[1];
2524             aNodes[4] = N1[3];
2525             aNodes[5] = N2[5];
2526             aNodes[6] = N2[3];
2527             aNodes[7] = N1[5];
2528             const SMDS_MeshElement* newElem = 0;
2529             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2530                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2531             myLastCreatedElems.Append(newElem);
2532             AddToSameGroups( newElem, tr1, aMesh );
2533             int aShapeId = tr1->getshapeId();
2534             if ( aShapeId )
2535               {
2536                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2537               }
2538             aMesh->RemoveElement( tr1 );
2539             aMesh->RemoveElement( tr3 );
2540             // remove middle node (9)
2541             GetMeshDS()->RemoveNode( N1[4] );
2542           }
2543         }
2544
2545         // Next element to fuse: the rejected one
2546         if ( tr3 )
2547           startElem = Ok12 ? tr3 : tr2;
2548
2549       } // if ( startElem )
2550     } // while ( startElem || !startLinks.empty() )
2551   } // while ( ! mapEl_setLi.empty() )
2552
2553   return true;
2554 }
2555
2556
2557 /*#define DUMPSO(txt) \
2558 //  cout << txt << endl;
2559 //=============================================================================
2560 //
2561 //
2562 //
2563 //=============================================================================
2564 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2565 {
2566 if ( i1 == i2 )
2567 return;
2568 int tmp = idNodes[ i1 ];
2569 idNodes[ i1 ] = idNodes[ i2 ];
2570 idNodes[ i2 ] = tmp;
2571 gp_Pnt Ptmp = P[ i1 ];
2572 P[ i1 ] = P[ i2 ];
2573 P[ i2 ] = Ptmp;
2574 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2575 }
2576
2577 //=======================================================================
2578 //function : SortQuadNodes
2579 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2580 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2581 //           1 or 2 else 0.
2582 //=======================================================================
2583
2584 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2585 int               idNodes[] )
2586 {
2587   gp_Pnt P[4];
2588   int i;
2589   for ( i = 0; i < 4; i++ ) {
2590     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2591     if ( !n ) return 0;
2592     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2593   }
2594
2595   gp_Vec V1(P[0], P[1]);
2596   gp_Vec V2(P[0], P[2]);
2597   gp_Vec V3(P[0], P[3]);
2598
2599   gp_Vec Cross1 = V1 ^ V2;
2600   gp_Vec Cross2 = V2 ^ V3;
2601
2602   i = 0;
2603   if (Cross1.Dot(Cross2) < 0)
2604   {
2605     Cross1 = V2 ^ V1;
2606     Cross2 = V1 ^ V3;
2607
2608     if (Cross1.Dot(Cross2) < 0)
2609       i = 2;
2610     else
2611       i = 1;
2612     swap ( i, i + 1, idNodes, P );
2613
2614     //     for ( int ii = 0; ii < 4; ii++ ) {
2615     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2616     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2617     //     }
2618   }
2619   return i;
2620 }
2621
2622 //=======================================================================
2623 //function : SortHexaNodes
2624 //purpose  : Set 8 nodes of a hexahedron in a good order.
2625 //           Return success status
2626 //=======================================================================
2627
2628 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2629                                       int               idNodes[] )
2630 {
2631   gp_Pnt P[8];
2632   int i;
2633   DUMPSO( "INPUT: ========================================");
2634   for ( i = 0; i < 8; i++ ) {
2635     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2636     if ( !n ) return false;
2637     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2638     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2639   }
2640   DUMPSO( "========================================");
2641
2642
2643   set<int> faceNodes;  // ids of bottom face nodes, to be found
2644   set<int> checkedId1; // ids of tried 2-nd nodes
2645   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2646   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2647   int iMin, iLoop1 = 0;
2648
2649   // Loop to try the 2-nd nodes
2650
2651   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2652   {
2653     // Find not checked 2-nd node
2654     for ( i = 1; i < 8; i++ )
2655       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2656         int id1 = idNodes[i];
2657         swap ( 1, i, idNodes, P );
2658         checkedId1.insert ( id1 );
2659         break;
2660       }
2661
2662     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2663     // ie that all but meybe one (id3 which is on the same face) nodes
2664     // lay on the same side from the triangle plane.
2665
2666     bool manyInPlane = false; // more than 4 nodes lay in plane
2667     int iLoop2 = 0;
2668     while ( ++iLoop2 < 6 ) {
2669
2670       // get 1-2-3 plane coeffs
2671       Standard_Real A, B, C, D;
2672       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2673       if ( N.SquareMagnitude() > gp::Resolution() )
2674       {
2675         gp_Pln pln ( P[0], N );
2676         pln.Coefficients( A, B, C, D );
2677
2678         // find the node (iMin) closest to pln
2679         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2680         set<int> idInPln;
2681         for ( i = 3; i < 8; i++ ) {
2682           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2683           if ( fabs( dist[i] ) < minDist ) {
2684             minDist = fabs( dist[i] );
2685             iMin = i;
2686           }
2687           if ( fabs( dist[i] ) <= tol )
2688             idInPln.insert( idNodes[i] );
2689         }
2690
2691         // there should not be more than 4 nodes in bottom plane
2692         if ( idInPln.size() > 1 )
2693         {
2694           DUMPSO( "### idInPln.size() = " << idInPln.size());
2695           // idInPlane does not contain the first 3 nodes
2696           if ( manyInPlane || idInPln.size() == 5)
2697             return false; // all nodes in one plane
2698           manyInPlane = true;
2699
2700           // set the 1-st node to be not in plane
2701           for ( i = 3; i < 8; i++ ) {
2702             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2703               DUMPSO( "### Reset 0-th node");
2704               swap( 0, i, idNodes, P );
2705               break;
2706             }
2707           }
2708
2709           // reset to re-check second nodes
2710           leastDist = DBL_MAX;
2711           faceNodes.clear();
2712           checkedId1.clear();
2713           iLoop1 = 0;
2714           break; // from iLoop2;
2715         }
2716
2717         // check that the other 4 nodes are on the same side
2718         bool sameSide = true;
2719         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2720         for ( i = 3; sameSide && i < 8; i++ ) {
2721           if ( i != iMin )
2722             sameSide = ( isNeg == dist[i] <= 0.);
2723         }
2724
2725         // keep best solution
2726         if ( sameSide && minDist < leastDist ) {
2727           leastDist = minDist;
2728           faceNodes.clear();
2729           faceNodes.insert( idNodes[ 1 ] );
2730           faceNodes.insert( idNodes[ 2 ] );
2731           faceNodes.insert( idNodes[ iMin ] );
2732           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2733                   << " leastDist = " << leastDist);
2734           if ( leastDist <= DBL_MIN )
2735             break;
2736         }
2737       }
2738
2739       // set next 3-d node to check
2740       int iNext = 2 + iLoop2;
2741       if ( iNext < 8 ) {
2742         DUMPSO( "Try 2-nd");
2743         swap ( 2, iNext, idNodes, P );
2744       }
2745     } // while ( iLoop2 < 6 )
2746   } // iLoop1
2747
2748   if ( faceNodes.empty() ) return false;
2749
2750   // Put the faceNodes in proper places
2751   for ( i = 4; i < 8; i++ ) {
2752     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2753       // find a place to put
2754       int iTo = 1;
2755       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2756         iTo++;
2757       DUMPSO( "Set faceNodes");
2758       swap ( iTo, i, idNodes, P );
2759     }
2760   }
2761
2762
2763   // Set nodes of the found bottom face in good order
2764   DUMPSO( " Found bottom face: ");
2765   i = SortQuadNodes( theMesh, idNodes );
2766   if ( i ) {
2767     gp_Pnt Ptmp = P[ i ];
2768     P[ i ] = P[ i+1 ];
2769     P[ i+1 ] = Ptmp;
2770   }
2771   //   else
2772   //     for ( int ii = 0; ii < 4; ii++ ) {
2773   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2774   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2775   //    }
2776
2777   // Gravity center of the top and bottom faces
2778   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2779   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2780
2781   // Get direction from the bottom to the top face
2782   gp_Vec upDir ( aGCb, aGCt );
2783   Standard_Real upDirSize = upDir.Magnitude();
2784   if ( upDirSize <= gp::Resolution() ) return false;
2785   upDir / upDirSize;
2786
2787   // Assure that the bottom face normal points up
2788   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2789   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2790   if ( Nb.Dot( upDir ) < 0 ) {
2791     DUMPSO( "Reverse bottom face");
2792     swap( 1, 3, idNodes, P );
2793   }
2794
2795   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2796   Standard_Real minDist = DBL_MAX;
2797   for ( i = 4; i < 8; i++ ) {
2798     // projection of P[i] to the plane defined by P[0] and upDir
2799     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2800     Standard_Real sqDist = P[0].SquareDistance( Pp );
2801     if ( sqDist < minDist ) {
2802       minDist = sqDist;
2803       iMin = i;
2804     }
2805   }
2806   DUMPSO( "Set 4-th");
2807   swap ( 4, iMin, idNodes, P );
2808
2809   // Set nodes of the top face in good order
2810   DUMPSO( "Sort top face");
2811   i = SortQuadNodes( theMesh, &idNodes[4] );
2812   if ( i ) {
2813     i += 4;
2814     gp_Pnt Ptmp = P[ i ];
2815     P[ i ] = P[ i+1 ];
2816     P[ i+1 ] = Ptmp;
2817   }
2818
2819   // Assure that direction of the top face normal is from the bottom face
2820   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2821   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2822   if ( Nt.Dot( upDir ) < 0 ) {
2823     DUMPSO( "Reverse top face");
2824     swap( 5, 7, idNodes, P );
2825   }
2826
2827   //   DUMPSO( "OUTPUT: ========================================");
2828   //   for ( i = 0; i < 8; i++ ) {
2829   //     float *p = ugrid->GetPoint(idNodes[i]);
2830   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2831   //   }
2832
2833   return true;
2834 }*/
2835
2836 //================================================================================
2837 /*!
2838  * \brief Return nodes linked to the given one
2839  * \param theNode - the node
2840  * \param linkedNodes - the found nodes
2841  * \param type - the type of elements to check
2842  *
2843  * Medium nodes are ignored
2844  */
2845 //================================================================================
2846
2847 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2848                                        TIDSortedElemSet &   linkedNodes,
2849                                        SMDSAbs_ElementType  type )
2850 {
2851   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2852   while ( elemIt->more() )
2853   {
2854     const SMDS_MeshElement* elem = elemIt->next();
2855     if(elem->GetType() == SMDSAbs_0DElement)
2856       continue;
2857     
2858     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2859     if ( elem->GetType() == SMDSAbs_Volume )
2860     {
2861       SMDS_VolumeTool vol( elem );
2862       while ( nodeIt->more() ) {
2863         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2864         if ( theNode != n && vol.IsLinked( theNode, n ))
2865           linkedNodes.insert( n );
2866       }
2867     }
2868     else
2869     {
2870       for ( int i = 0; nodeIt->more(); ++i ) {
2871         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2872         if ( n == theNode ) {
2873           int iBefore = i - 1;
2874           int iAfter  = i + 1;
2875           if ( elem->IsQuadratic() ) {
2876             int nb = elem->NbNodes() / 2;
2877             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2878             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2879           }
2880           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2881           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2882         }
2883       }
2884     }
2885   }
2886 }
2887
2888 //=======================================================================
2889 //function : laplacianSmooth
2890 //purpose  : pulls theNode toward the center of surrounding nodes directly
2891 //           connected to that node along an element edge
2892 //=======================================================================
2893
2894 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2895                      const Handle(Geom_Surface)&          theSurface,
2896                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2897 {
2898   // find surrounding nodes
2899
2900   TIDSortedElemSet nodeSet;
2901   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2902
2903   // compute new coodrs
2904
2905   double coord[] = { 0., 0., 0. };
2906   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2907   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2908     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2909     if ( theSurface.IsNull() ) { // smooth in 3D
2910       coord[0] += node->X();
2911       coord[1] += node->Y();
2912       coord[2] += node->Z();
2913     }
2914     else { // smooth in 2D
2915       ASSERT( theUVMap.find( node ) != theUVMap.end() );
2916       gp_XY* uv = theUVMap[ node ];
2917       coord[0] += uv->X();
2918       coord[1] += uv->Y();
2919     }
2920   }
2921   int nbNodes = nodeSet.size();
2922   if ( !nbNodes )
2923     return;
2924   coord[0] /= nbNodes;
2925   coord[1] /= nbNodes;
2926
2927   if ( !theSurface.IsNull() ) {
2928     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2929     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2930     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2931     coord[0] = p3d.X();
2932     coord[1] = p3d.Y();
2933     coord[2] = p3d.Z();
2934   }
2935   else
2936     coord[2] /= nbNodes;
2937
2938   // move node
2939
2940   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2941 }
2942
2943 //=======================================================================
2944 //function : centroidalSmooth
2945 //purpose  : pulls theNode toward the element-area-weighted centroid of the
2946 //           surrounding elements
2947 //=======================================================================
2948
2949 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
2950                       const Handle(Geom_Surface)&          theSurface,
2951                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2952 {
2953   gp_XYZ aNewXYZ(0.,0.,0.);
2954   SMESH::Controls::Area anAreaFunc;
2955   double totalArea = 0.;
2956   int nbElems = 0;
2957
2958   // compute new XYZ
2959
2960   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2961   while ( elemIt->more() )
2962   {
2963     const SMDS_MeshElement* elem = elemIt->next();
2964     nbElems++;
2965
2966     gp_XYZ elemCenter(0.,0.,0.);
2967     SMESH::Controls::TSequenceOfXYZ aNodePoints;
2968     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2969     int nn = elem->NbNodes();
2970     if(elem->IsQuadratic()) nn = nn/2;
2971     int i=0;
2972     //while ( itN->more() ) {
2973     while ( i<nn ) {
2974       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2975       i++;
2976       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2977       aNodePoints.push_back( aP );
2978       if ( !theSurface.IsNull() ) { // smooth in 2D
2979         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2980         gp_XY* uv = theUVMap[ aNode ];
2981         aP.SetCoord( uv->X(), uv->Y(), 0. );
2982       }
2983       elemCenter += aP;
2984     }
2985     double elemArea = anAreaFunc.GetValue( aNodePoints );
2986     totalArea += elemArea;
2987     elemCenter /= nn;
2988     aNewXYZ += elemCenter * elemArea;
2989   }
2990   aNewXYZ /= totalArea;
2991   if ( !theSurface.IsNull() ) {
2992     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2993     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2994   }
2995
2996   // move node
2997
2998   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
2999 }
3000
3001 //=======================================================================
3002 //function : getClosestUV
3003 //purpose  : return UV of closest projection
3004 //=======================================================================
3005
3006 static bool getClosestUV (Extrema_GenExtPS& projector,
3007                           const gp_Pnt&     point,
3008                           gp_XY &           result)
3009 {
3010   projector.Perform( point );
3011   if ( projector.IsDone() ) {
3012     double u, v, minVal = DBL_MAX;
3013     for ( int i = projector.NbExt(); i > 0; i-- )
3014 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3015       if ( projector.SquareDistance( i ) < minVal ) {
3016         minVal = projector.SquareDistance( i );
3017 #else
3018       if ( projector.Value( i ) < minVal ) {
3019         minVal = projector.Value( i );
3020 #endif
3021         projector.Point( i ).Parameter( u, v );
3022       }
3023     result.SetCoord( u, v );
3024     return true;
3025   }
3026   return false;
3027 }
3028
3029 //=======================================================================
3030 //function : Smooth
3031 //purpose  : Smooth theElements during theNbIterations or until a worst
3032 //           element has aspect ratio <= theTgtAspectRatio.
3033 //           Aspect Ratio varies in range [1.0, inf].
3034 //           If theElements is empty, the whole mesh is smoothed.
3035 //           theFixedNodes contains additionally fixed nodes. Nodes built
3036 //           on edges and boundary nodes are always fixed.
3037 //=======================================================================
3038
3039 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3040                                set<const SMDS_MeshNode*> & theFixedNodes,
3041                                const SmoothMethod          theSmoothMethod,
3042                                const int                   theNbIterations,
3043                                double                      theTgtAspectRatio,
3044                                const bool                  the2D)
3045 {
3046   myLastCreatedElems.Clear();
3047   myLastCreatedNodes.Clear();
3048
3049   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3050
3051   if ( theTgtAspectRatio < 1.0 )
3052     theTgtAspectRatio = 1.0;
3053
3054   const double disttol = 1.e-16;
3055
3056   SMESH::Controls::AspectRatio aQualityFunc;
3057
3058   SMESHDS_Mesh* aMesh = GetMeshDS();
3059
3060   if ( theElems.empty() ) {
3061     // add all faces to theElems
3062     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3063     while ( fIt->more() ) {
3064       const SMDS_MeshElement* face = fIt->next();
3065       theElems.insert( face );
3066     }
3067   }
3068   // get all face ids theElems are on
3069   set< int > faceIdSet;
3070   TIDSortedElemSet::iterator itElem;
3071   if ( the2D )
3072     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3073       int fId = FindShape( *itElem );
3074       // check that corresponding submesh exists and a shape is face
3075       if (fId &&
3076           faceIdSet.find( fId ) == faceIdSet.end() &&
3077           aMesh->MeshElements( fId )) {
3078         TopoDS_Shape F = aMesh->IndexToShape( fId );
3079         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3080           faceIdSet.insert( fId );
3081       }
3082     }
3083   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3084
3085   // ===============================================
3086   // smooth elements on each TopoDS_Face separately
3087   // ===============================================
3088
3089   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3090   for ( ; fId != faceIdSet.rend(); ++fId ) {
3091     // get face surface and submesh
3092     Handle(Geom_Surface) surface;
3093     SMESHDS_SubMesh* faceSubMesh = 0;
3094     TopoDS_Face face;
3095     double fToler2 = 0, f,l;
3096     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3097     bool isUPeriodic = false, isVPeriodic = false;
3098     if ( *fId ) {
3099       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3100       surface = BRep_Tool::Surface( face );
3101       faceSubMesh = aMesh->MeshElements( *fId );
3102       fToler2 = BRep_Tool::Tolerance( face );
3103       fToler2 *= fToler2 * 10.;
3104       isUPeriodic = surface->IsUPeriodic();
3105       if ( isUPeriodic )
3106         surface->UPeriod();
3107       isVPeriodic = surface->IsVPeriodic();
3108       if ( isVPeriodic )
3109         surface->VPeriod();
3110       surface->Bounds( u1, u2, v1, v2 );
3111     }
3112     // ---------------------------------------------------------
3113     // for elements on a face, find movable and fixed nodes and
3114     // compute UV for them
3115     // ---------------------------------------------------------
3116     bool checkBoundaryNodes = false;
3117     bool isQuadratic = false;
3118     set<const SMDS_MeshNode*> setMovableNodes;
3119     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3120     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3121     list< const SMDS_MeshElement* > elemsOnFace;
3122
3123     Extrema_GenExtPS projector;
3124     GeomAdaptor_Surface surfAdaptor;
3125     if ( !surface.IsNull() ) {
3126       surfAdaptor.Load( surface );
3127       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3128     }
3129     int nbElemOnFace = 0;
3130     itElem = theElems.begin();
3131     // loop on not yet smoothed elements: look for elems on a face
3132     while ( itElem != theElems.end() ) {
3133       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3134         break; // all elements found
3135
3136       const SMDS_MeshElement* elem = *itElem;
3137       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3138            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3139         ++itElem;
3140         continue;
3141       }
3142       elemsOnFace.push_back( elem );
3143       theElems.erase( itElem++ );
3144       nbElemOnFace++;
3145
3146       if ( !isQuadratic )
3147         isQuadratic = elem->IsQuadratic();
3148
3149       // get movable nodes of elem
3150       const SMDS_MeshNode* node;
3151       SMDS_TypeOfPosition posType;
3152       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3153       int nn = 0, nbn =  elem->NbNodes();
3154       if(elem->IsQuadratic())
3155         nbn = nbn/2;
3156       while ( nn++ < nbn ) {
3157         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3158         const SMDS_PositionPtr& pos = node->GetPosition();
3159         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3160         if (posType != SMDS_TOP_EDGE &&
3161             posType != SMDS_TOP_VERTEX &&
3162             theFixedNodes.find( node ) == theFixedNodes.end())
3163         {
3164           // check if all faces around the node are on faceSubMesh
3165           // because a node on edge may be bound to face
3166           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3167           bool all = true;
3168           if ( faceSubMesh ) {
3169             while ( eIt->more() && all ) {
3170               const SMDS_MeshElement* e = eIt->next();
3171               all = faceSubMesh->Contains( e );
3172             }
3173           }
3174           if ( all )
3175             setMovableNodes.insert( node );
3176           else
3177             checkBoundaryNodes = true;
3178         }
3179         if ( posType == SMDS_TOP_3DSPACE )
3180           checkBoundaryNodes = true;
3181       }
3182
3183       if ( surface.IsNull() )
3184         continue;
3185
3186       // get nodes to check UV
3187       list< const SMDS_MeshNode* > uvCheckNodes;
3188       itN = elem->nodesIterator();
3189       nn = 0; nbn =  elem->NbNodes();
3190       if(elem->IsQuadratic())
3191         nbn = nbn/2;
3192       while ( nn++ < nbn ) {
3193         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3194         if ( uvMap.find( node ) == uvMap.end() )
3195           uvCheckNodes.push_back( node );
3196         // add nodes of elems sharing node
3197         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3198         //         while ( eIt->more() ) {
3199         //           const SMDS_MeshElement* e = eIt->next();
3200         //           if ( e != elem ) {
3201         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3202         //             while ( nIt->more() ) {
3203         //               const SMDS_MeshNode* n =
3204         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3205         //               if ( uvMap.find( n ) == uvMap.end() )
3206         //                 uvCheckNodes.push_back( n );
3207         //             }
3208         //           }
3209         //         }
3210       }
3211       // check UV on face
3212       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3213       for ( ; n != uvCheckNodes.end(); ++n ) {
3214         node = *n;
3215         gp_XY uv( 0, 0 );
3216         const SMDS_PositionPtr& pos = node->GetPosition();
3217         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3218         // get existing UV
3219         switch ( posType ) {
3220         case SMDS_TOP_FACE: {
3221           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3222           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3223           break;
3224         }
3225         case SMDS_TOP_EDGE: {
3226           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3227           Handle(Geom2d_Curve) pcurve;
3228           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3229             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3230           if ( !pcurve.IsNull() ) {
3231             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3232             uv = pcurve->Value( u ).XY();
3233           }
3234           break;
3235         }
3236         case SMDS_TOP_VERTEX: {
3237           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3238           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3239             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3240           break;
3241         }
3242         default:;
3243         }
3244         // check existing UV
3245         bool project = true;
3246         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3247         double dist1 = DBL_MAX, dist2 = 0;
3248         if ( posType != SMDS_TOP_3DSPACE ) {
3249           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3250           project = dist1 > fToler2;
3251         }
3252         if ( project ) { // compute new UV
3253           gp_XY newUV;
3254           if ( !getClosestUV( projector, pNode, newUV )) {
3255             MESSAGE("Node Projection Failed " << node);
3256           }
3257           else {
3258             if ( isUPeriodic )
3259               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3260             if ( isVPeriodic )
3261               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3262             // check new UV
3263             if ( posType != SMDS_TOP_3DSPACE )
3264               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3265             if ( dist2 < dist1 )
3266               uv = newUV;
3267           }
3268         }
3269         // store UV in the map
3270         listUV.push_back( uv );
3271         uvMap.insert( make_pair( node, &listUV.back() ));
3272       }
3273     } // loop on not yet smoothed elements
3274
3275     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3276       checkBoundaryNodes = true;
3277
3278     // fix nodes on mesh boundary
3279
3280     if ( checkBoundaryNodes ) {
3281       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3282       map< SMESH_TLink, int >::iterator link_nb;
3283       // put all elements links to linkNbMap
3284       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3285       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3286         const SMDS_MeshElement* elem = (*elemIt);
3287         int nbn =  elem->NbCornerNodes();
3288         // loop on elem links: insert them in linkNbMap
3289         for ( int iN = 0; iN < nbn; ++iN ) {
3290           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3291           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3292           SMESH_TLink link( n1, n2 );
3293           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3294           link_nb->second++;
3295         }
3296       }
3297       // remove nodes that are in links encountered only once from setMovableNodes
3298       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3299         if ( link_nb->second == 1 ) {
3300           setMovableNodes.erase( link_nb->first.node1() );
3301           setMovableNodes.erase( link_nb->first.node2() );
3302         }
3303       }
3304     }
3305
3306     // -----------------------------------------------------
3307     // for nodes on seam edge, compute one more UV ( uvMap2 );
3308     // find movable nodes linked to nodes on seam and which
3309     // are to be smoothed using the second UV ( uvMap2 )
3310     // -----------------------------------------------------
3311
3312     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3313     if ( !surface.IsNull() ) {
3314       TopExp_Explorer eExp( face, TopAbs_EDGE );
3315       for ( ; eExp.More(); eExp.Next() ) {
3316         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3317         if ( !BRep_Tool::IsClosed( edge, face ))
3318           continue;
3319         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3320         if ( !sm ) continue;
3321         // find out which parameter varies for a node on seam
3322         double f,l;
3323         gp_Pnt2d uv1, uv2;
3324         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3325         if ( pcurve.IsNull() ) continue;
3326         uv1 = pcurve->Value( f );
3327         edge.Reverse();
3328         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3329         if ( pcurve.IsNull() ) continue;
3330         uv2 = pcurve->Value( f );
3331         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3332         // assure uv1 < uv2
3333         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3334           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3335         }
3336         // get nodes on seam and its vertices
3337         list< const SMDS_MeshNode* > seamNodes;
3338         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3339         while ( nSeamIt->more() ) {
3340           const SMDS_MeshNode* node = nSeamIt->next();
3341           if ( !isQuadratic || !IsMedium( node ))
3342             seamNodes.push_back( node );
3343         }
3344         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3345         for ( ; vExp.More(); vExp.Next() ) {
3346           sm = aMesh->MeshElements( vExp.Current() );
3347           if ( sm ) {
3348             nSeamIt = sm->GetNodes();
3349             while ( nSeamIt->more() )
3350               seamNodes.push_back( nSeamIt->next() );
3351           }
3352         }
3353         // loop on nodes on seam
3354         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3355         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3356           const SMDS_MeshNode* nSeam = *noSeIt;
3357           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3358           if ( n_uv == uvMap.end() )
3359             continue;
3360           // set the first UV
3361           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3362           // set the second UV
3363           listUV.push_back( *n_uv->second );
3364           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3365           if ( uvMap2.empty() )
3366             uvMap2 = uvMap; // copy the uvMap contents
3367           uvMap2[ nSeam ] = &listUV.back();
3368
3369           // collect movable nodes linked to ones on seam in nodesNearSeam
3370           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3371           while ( eIt->more() ) {
3372             const SMDS_MeshElement* e = eIt->next();
3373             int nbUseMap1 = 0, nbUseMap2 = 0;
3374             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3375             int nn = 0, nbn =  e->NbNodes();
3376             if(e->IsQuadratic()) nbn = nbn/2;
3377             while ( nn++ < nbn )
3378             {
3379               const SMDS_MeshNode* n =
3380                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3381               if (n == nSeam ||
3382                   setMovableNodes.find( n ) == setMovableNodes.end() )
3383                 continue;
3384               // add only nodes being closer to uv2 than to uv1
3385               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3386                            0.5 * ( n->Y() + nSeam->Y() ),
3387                            0.5 * ( n->Z() + nSeam->Z() ));
3388               gp_XY uv;
3389               getClosestUV( projector, pMid, uv );
3390               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3391                 nodesNearSeam.insert( n );
3392                 nbUseMap2++;
3393               }
3394               else
3395                 nbUseMap1++;
3396             }
3397             // for centroidalSmooth all element nodes must
3398             // be on one side of a seam
3399             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3400               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3401               nn = 0;
3402               while ( nn++ < nbn ) {
3403                 const SMDS_MeshNode* n =
3404                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3405                 setMovableNodes.erase( n );
3406               }
3407             }
3408           }
3409         } // loop on nodes on seam
3410       } // loop on edge of a face
3411     } // if ( !face.IsNull() )
3412
3413     if ( setMovableNodes.empty() ) {
3414       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3415       continue; // goto next face
3416     }
3417
3418     // -------------
3419     // SMOOTHING //
3420     // -------------
3421
3422     int it = -1;
3423     double maxRatio = -1., maxDisplacement = -1.;
3424     set<const SMDS_MeshNode*>::iterator nodeToMove;
3425     for ( it = 0; it < theNbIterations; it++ ) {
3426       maxDisplacement = 0.;
3427       nodeToMove = setMovableNodes.begin();
3428       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3429         const SMDS_MeshNode* node = (*nodeToMove);
3430         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3431
3432         // smooth
3433         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3434         if ( theSmoothMethod == LAPLACIAN )
3435           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3436         else
3437           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3438
3439         // node displacement
3440         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3441         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3442         if ( aDispl > maxDisplacement )
3443           maxDisplacement = aDispl;
3444       }
3445       // no node movement => exit
3446       //if ( maxDisplacement < 1.e-16 ) {
3447       if ( maxDisplacement < disttol ) {
3448         MESSAGE("-- no node movement --");
3449         break;
3450       }
3451
3452       // check elements quality
3453       maxRatio  = 0;
3454       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3455       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3456         const SMDS_MeshElement* elem = (*elemIt);
3457         if ( !elem || elem->GetType() != SMDSAbs_Face )
3458           continue;
3459         SMESH::Controls::TSequenceOfXYZ aPoints;
3460         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3461           double aValue = aQualityFunc.GetValue( aPoints );
3462           if ( aValue > maxRatio )
3463             maxRatio = aValue;
3464         }
3465       }
3466       if ( maxRatio <= theTgtAspectRatio ) {
3467         MESSAGE("-- quality achived --");
3468         break;
3469       }
3470       if (it+1 == theNbIterations) {
3471         MESSAGE("-- Iteration limit exceeded --");
3472       }
3473     } // smoothing iterations
3474
3475     MESSAGE(" Face id: " << *fId <<
3476             " Nb iterstions: " << it <<
3477             " Displacement: " << maxDisplacement <<
3478             " Aspect Ratio " << maxRatio);
3479
3480     // ---------------------------------------
3481     // new nodes positions are computed,
3482     // record movement in DS and set new UV
3483     // ---------------------------------------
3484     nodeToMove = setMovableNodes.begin();
3485     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3486       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3487       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3488       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3489       if ( node_uv != uvMap.end() ) {
3490         gp_XY* uv = node_uv->second;
3491         node->SetPosition
3492           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3493       }
3494     }
3495
3496     // move medium nodes of quadratic elements
3497     if ( isQuadratic )
3498     {
3499       SMESH_MesherHelper helper( *GetMesh() );
3500       if ( !face.IsNull() )
3501         helper.SetSubShape( face );
3502       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3503       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3504         const SMDS_VtkFace* QF =
3505           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3506         if(QF && QF->IsQuadratic()) {
3507           vector<const SMDS_MeshNode*> Ns;
3508           Ns.reserve(QF->NbNodes()+1);
3509           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3510           while ( anIter->more() )
3511             Ns.push_back( cast2Node(anIter->next()) );
3512           Ns.push_back( Ns[0] );
3513           double x, y, z;
3514           for(int i=0; i<QF->NbNodes(); i=i+2) {
3515             if ( !surface.IsNull() ) {
3516               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3517               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3518               gp_XY uv = ( uv1 + uv2 ) / 2.;
3519               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3520               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3521             }
3522             else {
3523               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3524               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3525               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3526             }
3527             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3528                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3529                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3530               // we have to move i+1 node
3531               aMesh->MoveNode( Ns[i+1], x, y, z );
3532             }
3533           }
3534         }
3535       }
3536     }
3537
3538   } // loop on face ids
3539
3540 }
3541
3542 //=======================================================================
3543 //function : isReverse
3544 //purpose  : Return true if normal of prevNodes is not co-directied with
3545 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3546 //           iNotSame is where prevNodes and nextNodes are different.
3547 //           If result is true then future volume orientation is OK
3548 //=======================================================================
3549
3550 static bool isReverse(const SMDS_MeshElement*             face,
3551                       const vector<const SMDS_MeshNode*>& prevNodes,
3552                       const vector<const SMDS_MeshNode*>& nextNodes,
3553                       const int                           iNotSame)
3554 {
3555
3556   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3557   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3558   gp_XYZ extrDir( pN - pP ), faceNorm;
3559   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3560
3561   return faceNorm * extrDir < 0.0;
3562 }
3563
3564 //=======================================================================
3565 /*!
3566  * \brief Create elements by sweeping an element
3567  * \param elem - element to sweep
3568  * \param newNodesItVec - nodes generated from each node of the element
3569  * \param newElems - generated elements
3570  * \param nbSteps - number of sweeping steps
3571  * \param srcElements - to append elem for each generated element
3572  */
3573 //=======================================================================
3574
3575 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3576                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3577                                     list<const SMDS_MeshElement*>&        newElems,
3578                                     const int                             nbSteps,
3579                                     SMESH_SequenceOfElemPtr&              srcElements)
3580 {
3581   //MESSAGE("sweepElement " << nbSteps);
3582   SMESHDS_Mesh* aMesh = GetMeshDS();
3583
3584   const int           nbNodes = elem->NbNodes();          
3585   const int         nbCorners = elem->NbCornerNodes();
3586   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of 
3587                                                           polyhedron creation !!! */
3588   // Loop on elem nodes:
3589   // find new nodes and detect same nodes indices
3590   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3591   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3592   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3593   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3594
3595   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3596   vector<int> sames(nbNodes);
3597   vector<bool> isSingleNode(nbNodes);
3598
3599   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3600     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3601     const SMDS_MeshNode*                         node = nnIt->first;
3602     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3603     if ( listNewNodes.empty() )
3604       return;
3605
3606     itNN   [ iNode ] = listNewNodes.begin();
3607     prevNod[ iNode ] = node;
3608     nextNod[ iNode ] = listNewNodes.front();
3609
3610     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3611                                                              corner node of linear */
3612     if ( prevNod[ iNode ] != nextNod [ iNode ])
3613       nbDouble += !isSingleNode[iNode];
3614
3615     if( iNode < nbCorners ) { // check corners only
3616       if ( prevNod[ iNode ] == nextNod [ iNode ])
3617         sames[nbSame++] = iNode;
3618       else
3619         iNotSameNode = iNode;
3620     }
3621   }
3622
3623   if ( nbSame == nbNodes || nbSame > 2) {
3624     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3625     return;
3626   }
3627
3628   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3629   {
3630     // fix nodes order to have bottom normal external
3631     if ( baseType == SMDSEntity_Polygon )
3632     {
3633       std::reverse( itNN.begin(), itNN.end() );
3634       std::reverse( prevNod.begin(), prevNod.end() );
3635       std::reverse( midlNod.begin(), midlNod.end() );
3636       std::reverse( nextNod.begin(), nextNod.end() );
3637       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3638     }
3639     else
3640     {
3641       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3642       SMDS_MeshCell::applyInterlace( ind, itNN );
3643       SMDS_MeshCell::applyInterlace( ind, prevNod );
3644       SMDS_MeshCell::applyInterlace( ind, nextNod );
3645       SMDS_MeshCell::applyInterlace( ind, midlNod );
3646       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3647       if ( nbSame > 0 )
3648       {
3649         sames[nbSame] = iNotSameNode;
3650         for ( int j = 0; j <= nbSame; ++j )
3651           for ( size_t i = 0; i < ind.size(); ++i )
3652             if ( ind[i] == sames[j] )
3653             {
3654               sames[j] = i;
3655               break;
3656             }
3657         iNotSameNode = sames[nbSame];
3658       }
3659     }
3660   }
3661
3662   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3663   if ( nbSame > 0 ) {
3664     iSameNode    = sames[ nbSame-1 ];
3665     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3666     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3667     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3668   }
3669
3670   // make new elements
3671   for (int iStep = 0; iStep < nbSteps; iStep++ )
3672   {
3673     // get next nodes
3674     for ( iNode = 0; iNode < nbNodes; iNode++ )
3675     {
3676       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3677       nextNod[ iNode ] = *itNN[ iNode ]++;
3678     }
3679
3680     SMDS_MeshElement* aNewElem = 0;
3681     /*if(!elem->IsPoly())*/ {
3682       switch ( baseType ) {
3683       case SMDSEntity_0D:
3684       case SMDSEntity_Node: { // sweep NODE
3685         if ( nbSame == 0 ) {
3686           if ( isSingleNode[0] )
3687             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3688           else
3689             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3690         }
3691         else
3692           return;
3693         break;
3694       }
3695       case SMDSEntity_Edge: { // sweep EDGE
3696         if ( nbDouble == 0 )
3697         {
3698           if ( nbSame == 0 ) // ---> quadrangle
3699             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3700                                       nextNod[ 1 ], nextNod[ 0 ] );
3701           else               // ---> triangle
3702             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3703                                       nextNod[ iNotSameNode ] );
3704         }
3705         else                 // ---> polygon
3706         {
3707           vector<const SMDS_MeshNode*> poly_nodes;
3708           poly_nodes.push_back( prevNod[0] );
3709           poly_nodes.push_back( prevNod[1] );
3710           if ( prevNod[1] != nextNod[1] )
3711           {
3712             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3713             poly_nodes.push_back( nextNod[1] );
3714           }
3715           if ( prevNod[0] != nextNod[0] )
3716           {
3717             poly_nodes.push_back( nextNod[0] );
3718             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3719           }
3720           switch ( poly_nodes.size() ) {
3721           case 3:
3722             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3723             break;
3724           case 4:
3725             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3726                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3727             break;
3728           default:
3729             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3730           }
3731         }
3732         break;
3733       }
3734       case SMDSEntity_Triangle: // TRIANGLE --->
3735         {
3736           if ( nbDouble > 0 ) break;
3737           if ( nbSame == 0 )       // ---> pentahedron
3738             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3739                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3740
3741           else if ( nbSame == 1 )  // ---> pyramid
3742             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3743                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3744                                          nextNod[ iSameNode ]);
3745
3746           else // 2 same nodes:       ---> tetrahedron
3747             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3748                                          nextNod[ iNotSameNode ]);
3749           break;
3750         }
3751       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3752         {
3753           if ( nbSame == 2 )
3754             return;
3755           if ( nbDouble+nbSame == 2 )
3756           {
3757             if(nbSame==0) {      // ---> quadratic quadrangle
3758               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3759                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3760             }
3761             else { //(nbSame==1) // ---> quadratic triangle
3762               if(sames[0]==2) {
3763                 return; // medium node on axis
3764               }
3765               else if(sames[0]==0)
3766                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3767                                           nextNod[2], midlNod[1], prevNod[2]);
3768               else // sames[0]==1
3769                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3770                                           midlNod[0], nextNod[2], prevNod[2]);
3771             }
3772           }
3773           else if ( nbDouble == 3 )
3774           {
3775             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3776               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3777                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3778             }
3779           }
3780           else
3781             return;
3782           break;
3783         }
3784       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3785         if ( nbDouble > 0 ) break;
3786
3787         if ( nbSame == 0 )       // ---> hexahedron
3788           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3789                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3790
3791         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3792           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3793                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3794                                        nextNod[ iSameNode ]);
3795           newElems.push_back( aNewElem );
3796           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3797                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3798                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3799         }
3800         else if ( nbSame == 2 ) { // ---> pentahedron
3801           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3802             // iBeforeSame is same too
3803             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3804                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3805                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3806           else
3807             // iAfterSame is same too
3808             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3809                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3810                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3811         }
3812         break;
3813       }
3814       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3815         if ( nbDouble+nbSame != 3 ) break;
3816         if(nbSame==0) {
3817           // --->  pentahedron with 15 nodes
3818           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3819                                        nextNod[0], nextNod[1], nextNod[2],
3820                                        prevNod[3], prevNod[4], prevNod[5],
3821                                        nextNod[3], nextNod[4], nextNod[5],
3822                                        midlNod[0], midlNod[1], midlNod[2]);
3823         }
3824         else if(nbSame==1) {
3825           // --->  2d order pyramid of 13 nodes
3826           int apex = iSameNode;
3827           int i0 = ( apex + 1 ) % nbCorners;
3828           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3829           int i0a = apex + 3;
3830           int i1a = i1 + 3;
3831           int i01 = i0 + 3;
3832           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3833                                       nextNod[i0], nextNod[i1], prevNod[apex],
3834                                       prevNod[i01], midlNod[i0],
3835                                       nextNod[i01], midlNod[i1],
3836                                       prevNod[i1a], prevNod[i0a],
3837                                       nextNod[i0a], nextNod[i1a]);
3838         }
3839         else if(nbSame==2) {
3840           // --->  2d order tetrahedron of 10 nodes
3841           int n1 = iNotSameNode;
3842           int n2 = ( n1 + 1             ) % nbCorners;
3843           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3844           int n12 = n1 + 3;
3845           int n23 = n2 + 3;
3846           int n31 = n3 + 3;
3847           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3848                                        prevNod[n12], prevNod[n23], prevNod[n31],
3849                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3850         }
3851         break;
3852       }
3853       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3854         if( nbSame == 0 ) {
3855           if ( nbDouble != 4 ) break;
3856           // --->  hexahedron with 20 nodes
3857           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3858                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3859                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3860                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3861                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3862         }
3863         else if(nbSame==1) {
3864           // ---> pyramid + pentahedron - can not be created since it is needed 
3865           // additional middle node at the center of face
3866           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3867           return;
3868         }
3869         else if( nbSame == 2 ) {
3870           if ( nbDouble != 2 ) break;
3871           // --->  2d order Pentahedron with 15 nodes
3872           int n1,n2,n4,n5;
3873           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3874             // iBeforeSame is same too
3875             n1 = iBeforeSame;
3876             n2 = iOpposSame;
3877             n4 = iSameNode;
3878             n5 = iAfterSame;
3879           }
3880           else {
3881             // iAfterSame is same too
3882             n1 = iSameNode;
3883             n2 = iBeforeSame;
3884             n4 = iAfterSame;
3885             n5 = iOpposSame;
3886           }
3887           int n12 = n2 + 4;
3888           int n45 = n4 + 4;
3889           int n14 = n1 + 4;
3890           int n25 = n5 + 4;
3891           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3892                                        prevNod[n4], prevNod[n5], nextNod[n5],
3893                                        prevNod[n12], midlNod[n2], nextNod[n12],
3894                                        prevNod[n45], midlNod[n5], nextNod[n45],
3895                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3896         }
3897         break;
3898       }
3899       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3900
3901         if( nbSame == 0 && nbDouble == 9 ) {
3902           // --->  tri-quadratic hexahedron with 27 nodes
3903           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3904                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3905                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3906                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3907                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3908                                        prevNod[8], // bottom center
3909                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3910                                        nextNod[8], // top center
3911                                        midlNod[8]);// elem center
3912         }
3913         else
3914         {
3915           return;
3916         }
3917         break;
3918       }
3919       case SMDSEntity_Polygon: { // sweep POLYGON
3920
3921         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3922           // --->  hexagonal prism
3923           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3924                                        prevNod[3], prevNod[4], prevNod[5],
3925                                        nextNod[0], nextNod[1], nextNod[2],
3926                                        nextNod[3], nextNod[4], nextNod[5]);
3927         }
3928         break;
3929       }
3930       case SMDSEntity_Ball:
3931         return;
3932
3933       default:
3934         break;
3935       }
3936     }
3937
3938     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3939     {
3940       if ( baseType != SMDSEntity_Polygon )
3941       {
3942         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3943         SMDS_MeshCell::applyInterlace( ind, prevNod );
3944         SMDS_MeshCell::applyInterlace( ind, nextNod );
3945         SMDS_MeshCell::applyInterlace( ind, midlNod );
3946         SMDS_MeshCell::applyInterlace( ind, itNN );
3947         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3948         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
3949       }
3950       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3951       vector<int> quantities (nbNodes + 2);
3952       polyedre_nodes.clear();
3953       quantities.clear();
3954
3955       // bottom of prism
3956       for (int inode = 0; inode < nbNodes; inode++)
3957         polyedre_nodes.push_back( prevNod[inode] );
3958       quantities.push_back( nbNodes );
3959
3960       // top of prism
3961       polyedre_nodes.push_back( nextNod[0] );
3962       for (int inode = nbNodes; inode-1; --inode )
3963         polyedre_nodes.push_back( nextNod[inode-1] );
3964       quantities.push_back( nbNodes );
3965
3966       // side faces
3967       for (int iface = 0; iface < nbNodes; iface++)
3968       {
3969         const int prevNbNodes = polyedre_nodes.size();
3970         int inextface = (iface+1) % nbNodes;
3971         polyedre_nodes.push_back( prevNod[inextface] );
3972         polyedre_nodes.push_back( prevNod[iface] );
3973         if ( prevNod[iface] != nextNod[iface] )
3974         {
3975           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
3976           polyedre_nodes.push_back( nextNod[iface] );
3977         }
3978         if ( prevNod[inextface] != nextNod[inextface] )
3979         {
3980           polyedre_nodes.push_back( nextNod[inextface] );
3981           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
3982         }
3983         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
3984         if ( nbFaceNodes > 2 )
3985           quantities.push_back( nbFaceNodes );
3986         else // degenerated face
3987           polyedre_nodes.resize( prevNbNodes );
3988       }
3989       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3990     }
3991
3992     if ( aNewElem ) {
3993       newElems.push_back( aNewElem );
3994       myLastCreatedElems.Append(aNewElem);
3995       srcElements.Append( elem );
3996     }
3997
3998     // set new prev nodes
3999     for ( iNode = 0; iNode < nbNodes; iNode++ )
4000       prevNod[ iNode ] = nextNod[ iNode ];
4001
4002   } // for steps
4003 }
4004
4005 //=======================================================================
4006 /*!
4007  * \brief Create 1D and 2D elements around swept elements
4008  * \param mapNewNodes - source nodes and ones generated from them
4009  * \param newElemsMap - source elements and ones generated from them
4010  * \param elemNewNodesMap - nodes generated from each node of each element
4011  * \param elemSet - all swept elements
4012  * \param nbSteps - number of sweeping steps
4013  * \param srcElements - to append elem for each generated element
4014  */
4015 //=======================================================================
4016
4017 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4018                                   TElemOfElemListMap &     newElemsMap,
4019                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4020                                   TIDSortedElemSet&        elemSet,
4021                                   const int                nbSteps,
4022                                   SMESH_SequenceOfElemPtr& srcElements)
4023 {
4024   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4025   SMESHDS_Mesh* aMesh = GetMeshDS();
4026
4027   // Find nodes belonging to only one initial element - sweep them to get edges.
4028
4029   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4030   for ( ; nList != mapNewNodes.end(); nList++ )
4031   {
4032     const SMDS_MeshNode* node =
4033       static_cast<const SMDS_MeshNode*>( nList->first );
4034     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4035     int nbInitElems = 0;
4036     const SMDS_MeshElement* el = 0;
4037     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4038     while ( eIt->more() && nbInitElems < 2 ) {
4039       el = eIt->next();
4040       SMDSAbs_ElementType type = el->GetType();
4041       if ( type == SMDSAbs_Volume || type < highType ) continue;
4042       if ( type > highType ) {
4043         nbInitElems = 0;
4044         highType = type;
4045       }
4046       nbInitElems += elemSet.count(el);
4047     }
4048     if ( nbInitElems < 2 ) {
4049       bool NotCreateEdge = el && el->IsMediumNode(node);
4050       if(!NotCreateEdge) {
4051         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4052         list<const SMDS_MeshElement*> newEdges;
4053         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4054       }
4055     }
4056   }
4057
4058   // Make a ceiling for each element ie an equal element of last new nodes.
4059   // Find free links of faces - make edges and sweep them into faces.
4060
4061   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4062   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4063   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4064   {
4065     const SMDS_MeshElement* elem = itElem->first;
4066     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4067
4068     if(itElem->second.size()==0) continue;
4069
4070     const bool isQuadratic = elem->IsQuadratic();
4071
4072     if ( elem->GetType() == SMDSAbs_Edge ) {
4073       // create a ceiling edge
4074       if ( !isQuadratic ) {
4075         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4076                                vecNewNodes[ 1 ]->second.back())) {
4077           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4078                                                    vecNewNodes[ 1 ]->second.back()));
4079           srcElements.Append( myLastCreatedElems.Last() );
4080         }
4081       }
4082       else {
4083         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4084                                vecNewNodes[ 1 ]->second.back(),
4085                                vecNewNodes[ 2 ]->second.back())) {
4086           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4087                                                    vecNewNodes[ 1 ]->second.back(),
4088                                                    vecNewNodes[ 2 ]->second.back()));
4089           srcElements.Append( myLastCreatedElems.Last() );
4090         }
4091       }
4092     }
4093     if ( elem->GetType() != SMDSAbs_Face )
4094       continue;
4095
4096     bool hasFreeLinks = false;
4097
4098     TIDSortedElemSet avoidSet;
4099     avoidSet.insert( elem );
4100
4101     set<const SMDS_MeshNode*> aFaceLastNodes;
4102     int iNode, nbNodes = vecNewNodes.size();
4103     if ( !isQuadratic ) {
4104       // loop on the face nodes
4105       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4106         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4107         // look for free links of the face
4108         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4109         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4110         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4111         // check if a link is free
4112         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4113           hasFreeLinks = true;
4114           // make an edge and a ceiling for a new edge
4115           if ( !aMesh->FindEdge( n1, n2 )) {
4116             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4117             srcElements.Append( myLastCreatedElems.Last() );
4118           }
4119           n1 = vecNewNodes[ iNode ]->second.back();
4120           n2 = vecNewNodes[ iNext ]->second.back();
4121           if ( !aMesh->FindEdge( n1, n2 )) {
4122             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4123             srcElements.Append( myLastCreatedElems.Last() );
4124           }
4125         }
4126       }
4127     }
4128     else { // elem is quadratic face
4129       int nbn = nbNodes/2;
4130       for ( iNode = 0; iNode < nbn; iNode++ ) {
4131         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4132         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4133         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4134         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4135         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4136         // check if a link is free
4137         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4138              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4139              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4140           hasFreeLinks = true;
4141           // make an edge and a ceiling for a new edge
4142           // find medium node
4143           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4144             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4145             srcElements.Append( myLastCreatedElems.Last() );
4146           }
4147           n1 = vecNewNodes[ iNode ]->second.back();
4148           n2 = vecNewNodes[ iNext ]->second.back();
4149           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4150           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4151             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4152             srcElements.Append( myLastCreatedElems.Last() );
4153           }
4154         }
4155       }
4156       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4157         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4158       }
4159     }
4160
4161     // sweep free links into faces
4162
4163     if ( hasFreeLinks )  {
4164       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4165       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4166
4167       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4168       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4169         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4170         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4171       }
4172       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4173         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4174         std::advance( v, volNb );
4175         // find indices of free faces of a volume and their source edges
4176         list< int > freeInd;
4177         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4178         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4179         int iF, nbF = vTool.NbFaces();
4180         for ( iF = 0; iF < nbF; iF ++ ) {
4181           if (vTool.IsFreeFace( iF ) &&
4182               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4183               initNodeSet != faceNodeSet) // except an initial face
4184           {
4185             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4186               continue;
4187             freeInd.push_back( iF );
4188             // find source edge of a free face iF
4189             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4190             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4191             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4192                                    initNodeSet.begin(), initNodeSet.end(),
4193                                    commonNodes.begin());
4194             if ( (*v)->IsQuadratic() )
4195               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4196             else
4197               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4198 #ifdef _DEBUG_
4199             if ( !srcEdges.back() )
4200             {
4201               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4202                    << iF << " of volume #" << vTool.ID() << endl;
4203             }
4204 #endif
4205           }
4206         }
4207         if ( freeInd.empty() )
4208           continue;
4209
4210         // create faces for all steps;
4211         // if such a face has been already created by sweep of edge,
4212         // assure that its orientation is OK
4213         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4214           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4215           vTool.SetExternalNormal();
4216           const int nextShift = vTool.IsForward() ? +1 : -1;
4217           list< int >::iterator ind = freeInd.begin();
4218           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4219           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4220           {
4221             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4222             int nbn = vTool.NbFaceNodes( *ind );
4223             const SMDS_MeshElement * f = 0;
4224             if ( nbn == 3 )              ///// triangle
4225             {
4226               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4227               if ( !f ||
4228                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4229               {
4230                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4231                                                      nodes[ 1 ],
4232                                                      nodes[ 1 + nextShift ] };
4233                 if ( f )
4234                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4235                 else
4236                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4237                                                             newOrder[ 2 ] ));
4238               }
4239             }
4240             else if ( nbn == 4 )       ///// quadrangle
4241             {
4242               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4243               if ( !f ||
4244                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4245               {
4246                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4247                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4248                 if ( f )
4249                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4250                 else
4251                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4252                                                             newOrder[ 2 ], newOrder[ 3 ]));
4253               }
4254             }
4255             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4256             {
4257               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4258               if ( !f ||
4259                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4260               {
4261                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4262                                                      nodes[2],
4263                                                      nodes[2 + 2*nextShift],
4264                                                      nodes[3 - 2*nextShift],
4265                                                      nodes[3],
4266                                                      nodes[3 + 2*nextShift]};
4267                 if ( f )
4268                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4269                 else
4270                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4271                                                             newOrder[ 1 ],
4272                                                             newOrder[ 2 ],
4273                                                             newOrder[ 3 ],
4274                                                             newOrder[ 4 ],
4275                                                             newOrder[ 5 ] ));
4276               }
4277             }
4278             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4279             {
4280               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4281                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4282               if ( !f ||
4283                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4284               {
4285                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4286                                                      nodes[4 - 2*nextShift],
4287                                                      nodes[4],
4288                                                      nodes[4 + 2*nextShift],
4289                                                      nodes[1],
4290                                                      nodes[5 - 2*nextShift],
4291                                                      nodes[5],
4292                                                      nodes[5 + 2*nextShift] };
4293                 if ( f )
4294                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4295                 else
4296                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4297                                                            newOrder[ 2 ], newOrder[ 3 ],
4298                                                            newOrder[ 4 ], newOrder[ 5 ],
4299                                                            newOrder[ 6 ], newOrder[ 7 ]));
4300               }
4301             }
4302             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4303             {
4304               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4305                                       SMDSAbs_Face, /*noMedium=*/false);
4306               if ( !f ||
4307                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4308               {
4309                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4310                                                      nodes[4 - 2*nextShift],
4311                                                      nodes[4],
4312                                                      nodes[4 + 2*nextShift],
4313                                                      nodes[1],
4314                                                      nodes[5 - 2*nextShift],
4315                                                      nodes[5],
4316                                                      nodes[5 + 2*nextShift],
4317                                                      nodes[8] };
4318                 if ( f )
4319                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4320                 else
4321                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4322                                                            newOrder[ 2 ], newOrder[ 3 ],
4323                                                            newOrder[ 4 ], newOrder[ 5 ],
4324                                                            newOrder[ 6 ], newOrder[ 7 ],
4325                                                            newOrder[ 8 ]));
4326               }
4327             }
4328             else  //////// polygon
4329             {
4330               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4331               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4332               if ( !f ||
4333                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4334               {
4335                 if ( !vTool.IsForward() )
4336                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4337                 if ( f )
4338                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4339                 else
4340                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4341               }
4342             }
4343
4344             while ( srcElements.Length() < myLastCreatedElems.Length() )
4345               srcElements.Append( *srcEdge );
4346
4347           }  // loop on free faces
4348
4349           // go to the next volume
4350           iVol = 0;
4351           while ( iVol++ < nbVolumesByStep ) v++;
4352
4353         } // loop on steps
4354       } // loop on volumes of one step
4355     } // sweep free links into faces
4356
4357     // Make a ceiling face with a normal external to a volume
4358
4359     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4360
4361     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4362     if ( iF >= 0 ) {
4363       lastVol.SetExternalNormal();
4364       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4365       int nbn = lastVol.NbFaceNodes( iF );
4366       if ( nbn == 3 ) {
4367         if (!hasFreeLinks ||
4368             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4369           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4370       }
4371       else if ( nbn == 4 )
4372       {
4373         if (!hasFreeLinks ||
4374             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4375           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4376       }
4377       else if ( nbn == 6 && isQuadratic )
4378       {
4379         if (!hasFreeLinks ||
4380             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4381           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4382                                                    nodes[1], nodes[3], nodes[5]));
4383       }
4384       else if ( nbn == 8 && isQuadratic )
4385       {
4386         if (!hasFreeLinks ||
4387             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4388                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4389           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4390                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4391       }
4392       else if ( nbn == 9 && isQuadratic )
4393       {
4394         if (!hasFreeLinks ||
4395             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4396                                 SMDSAbs_Face, /*noMedium=*/false) )
4397           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4398                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4399                                                    nodes[8]));
4400       }
4401       else {
4402         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4403         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4404           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4405       }
4406
4407       while ( srcElements.Length() < myLastCreatedElems.Length() )
4408         srcElements.Append( myLastCreatedElems.Last() );
4409     }
4410   } // loop on swept elements
4411 }
4412
4413 //=======================================================================
4414 //function : RotationSweep
4415 //purpose  :
4416 //=======================================================================
4417
4418 SMESH_MeshEditor::PGroupIDs
4419 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4420                                 const gp_Ax1&      theAxis,
4421                                 const double       theAngle,
4422                                 const int          theNbSteps,
4423                                 const double       theTol,
4424                                 const bool         theMakeGroups,
4425                                 const bool         theMakeWalls)
4426 {
4427   myLastCreatedElems.Clear();
4428   myLastCreatedNodes.Clear();
4429
4430   // source elements for each generated one
4431   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4432
4433   MESSAGE( "RotationSweep()");
4434   gp_Trsf aTrsf;
4435   aTrsf.SetRotation( theAxis, theAngle );
4436   gp_Trsf aTrsf2;
4437   aTrsf2.SetRotation( theAxis, theAngle/2. );
4438
4439   gp_Lin aLine( theAxis );
4440   double aSqTol = theTol * theTol;
4441
4442   SMESHDS_Mesh* aMesh = GetMeshDS();
4443
4444   TNodeOfNodeListMap mapNewNodes;
4445   TElemOfVecOfNnlmiMap mapElemNewNodes;
4446   TElemOfElemListMap newElemsMap;
4447
4448   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4449                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4450                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4451   // loop on theElems
4452   TIDSortedElemSet::iterator itElem;
4453   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4454     const SMDS_MeshElement* elem = *itElem;
4455     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4456       continue;
4457     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4458     newNodesItVec.reserve( elem->NbNodes() );
4459
4460     // loop on elem nodes
4461     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4462     while ( itN->more() )
4463     {
4464       // check if a node has been already sweeped
4465       const SMDS_MeshNode* node = cast2Node( itN->next() );
4466
4467       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4468       double coord[3];
4469       aXYZ.Coord( coord[0], coord[1], coord[2] );
4470       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4471
4472       TNodeOfNodeListMapItr nIt =
4473         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4474       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4475       if ( listNewNodes.empty() )
4476       {
4477         // check if we are to create medium nodes between corner ones
4478         bool needMediumNodes = false;
4479         if ( isQuadraticMesh )
4480         {
4481           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4482           while (it->more() && !needMediumNodes )
4483           {
4484             const SMDS_MeshElement* invElem = it->next();
4485             if ( invElem != elem && !theElems.count( invElem )) continue;
4486             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4487             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4488               needMediumNodes = true;
4489           }
4490         }
4491
4492         // make new nodes
4493         const SMDS_MeshNode * newNode = node;
4494         for ( int i = 0; i < theNbSteps; i++ ) {
4495           if ( !isOnAxis ) {
4496             if ( needMediumNodes )  // create a medium node
4497             {
4498               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4499               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4500               myLastCreatedNodes.Append(newNode);
4501               srcNodes.Append( node );
4502               listNewNodes.push_back( newNode );
4503               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4504             }
4505             else {
4506               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4507             }
4508             // create a corner node
4509             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4510             myLastCreatedNodes.Append(newNode);
4511             srcNodes.Append( node );
4512             listNewNodes.push_back( newNode );
4513           }
4514           else {
4515             listNewNodes.push_back( newNode );
4516             // if ( needMediumNodes )
4517             //   listNewNodes.push_back( newNode );
4518           }
4519         }
4520       }
4521       newNodesItVec.push_back( nIt );
4522     }
4523     // make new elements
4524     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4525   }
4526
4527   if ( theMakeWalls )
4528     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4529
4530   PGroupIDs newGroupIDs;
4531   if ( theMakeGroups )
4532     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4533
4534   return newGroupIDs;
4535 }
4536
4537
4538 //=======================================================================
4539 //function : CreateNode
4540 //purpose  :
4541 //=======================================================================
4542 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4543                                                   const double y,
4544                                                   const double z,
4545                                                   const double tolnode,
4546                                                   SMESH_SequenceOfNode& aNodes)
4547 {
4548   // myLastCreatedElems.Clear();
4549   // myLastCreatedNodes.Clear();
4550
4551   gp_Pnt P1(x,y,z);
4552   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4553
4554   // try to search in sequence of existing nodes
4555   // if aNodes.Length()>0 we 'nave to use given sequence
4556   // else - use all nodes of mesh
4557   if(aNodes.Length()>0) {
4558     int i;
4559     for(i=1; i<=aNodes.Length(); i++) {
4560       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4561       if(P1.Distance(P2)<tolnode)
4562         return aNodes.Value(i);
4563     }
4564   }
4565   else {
4566     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4567     while(itn->more()) {
4568       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4569       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4570       if(P1.Distance(P2)<tolnode)
4571         return aN;
4572     }
4573   }
4574
4575   // create new node and return it
4576   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4577   //myLastCreatedNodes.Append(NewNode);
4578   return NewNode;
4579 }
4580
4581
4582 //=======================================================================
4583 //function : ExtrusionSweep
4584 //purpose  :
4585 //=======================================================================
4586
4587 SMESH_MeshEditor::PGroupIDs
4588 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4589                                   const gp_Vec&       theStep,
4590                                   const int           theNbSteps,
4591                                   TElemOfElemListMap& newElemsMap,
4592                                   const bool          theMakeGroups,
4593                                   const int           theFlags,
4594                                   const double        theTolerance)
4595 {
4596   ExtrusParam aParams;
4597   aParams.myDir = gp_Dir(theStep);
4598   aParams.myNodes.Clear();
4599   aParams.mySteps = new TColStd_HSequenceOfReal;
4600   int i;
4601   for(i=1; i<=theNbSteps; i++)
4602     aParams.mySteps->Append(theStep.Magnitude());
4603
4604   return
4605     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4606 }
4607
4608
4609 //=======================================================================
4610 //function : ExtrusionSweep
4611 //purpose  :
4612 //=======================================================================
4613
4614 SMESH_MeshEditor::PGroupIDs
4615 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4616                                   ExtrusParam&        theParams,
4617                                   TElemOfElemListMap& newElemsMap,
4618                                   const bool          theMakeGroups,
4619                                   const int           theFlags,
4620                                   const double        theTolerance)
4621 {
4622   myLastCreatedElems.Clear();
4623   myLastCreatedNodes.Clear();
4624
4625   // source elements for each generated one
4626   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4627
4628   SMESHDS_Mesh* aMesh = GetMeshDS();
4629
4630   int nbsteps = theParams.mySteps->Length();
4631
4632   TNodeOfNodeListMap mapNewNodes;
4633   //TNodeOfNodeVecMap mapNewNodes;
4634   TElemOfVecOfNnlmiMap mapElemNewNodes;
4635   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4636
4637   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4638                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4639                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4640   // loop on theElems
4641   TIDSortedElemSet::iterator itElem;
4642   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4643     // check element type
4644     const SMDS_MeshElement* elem = *itElem;
4645     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4646       continue;
4647
4648     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4649     newNodesItVec.reserve( elem->NbNodes() );
4650
4651     // loop on elem nodes
4652     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4653     while ( itN->more() )
4654     {
4655       // check if a node has been already sweeped
4656       const SMDS_MeshNode* node = cast2Node( itN->next() );
4657       TNodeOfNodeListMap::iterator nIt =
4658         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4659       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4660       if ( listNewNodes.empty() )
4661       {
4662         // make new nodes
4663
4664         // check if we are to create medium nodes between corner ones
4665         bool needMediumNodes = false;
4666         if ( isQuadraticMesh )
4667         {
4668           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4669           while (it->more() && !needMediumNodes )
4670           {
4671             const SMDS_MeshElement* invElem = it->next();
4672             if ( invElem != elem && !theElems.count( invElem )) continue;
4673             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4674             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4675               needMediumNodes = true;
4676           }
4677         }
4678
4679         double coord[] = { node->X(), node->Y(), node->Z() };
4680         for ( int i = 0; i < nbsteps; i++ )
4681         {
4682           if ( needMediumNodes ) // create a medium node
4683           {
4684             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4685             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4686             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4687             if( theFlags & EXTRUSION_FLAG_SEW ) {
4688               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4689                                                          theTolerance, theParams.myNodes);
4690               listNewNodes.push_back( newNode );
4691             }
4692             else {
4693               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4694               myLastCreatedNodes.Append(newNode);
4695               srcNodes.Append( node );
4696               listNewNodes.push_back( newNode );
4697             }
4698           }
4699           // create a corner node
4700           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4701           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4702           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4703           if( theFlags & EXTRUSION_FLAG_SEW ) {
4704             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4705                                                        theTolerance, theParams.myNodes);
4706             listNewNodes.push_back( newNode );
4707           }
4708           else {
4709             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4710             myLastCreatedNodes.Append(newNode);
4711             srcNodes.Append( node );
4712             listNewNodes.push_back( newNode );
4713           }
4714         }
4715       }
4716       newNodesItVec.push_back( nIt );
4717     }
4718     // make new elements
4719     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4720   }
4721
4722   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4723     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4724   }
4725   PGroupIDs newGroupIDs;
4726   if ( theMakeGroups )
4727     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4728
4729   return newGroupIDs;
4730 }
4731
4732 //=======================================================================
4733 //function : ExtrusionAlongTrack
4734 //purpose  :
4735 //=======================================================================
4736 SMESH_MeshEditor::Extrusion_Error
4737 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4738                                        SMESH_subMesh*       theTrack,
4739                                        const SMDS_MeshNode* theN1,
4740                                        const bool           theHasAngles,
4741                                        list<double>&        theAngles,
4742                                        const bool           theLinearVariation,
4743                                        const bool           theHasRefPoint,
4744                                        const gp_Pnt&        theRefPoint,
4745                                        const bool           theMakeGroups)
4746 {
4747   MESSAGE("ExtrusionAlongTrack");
4748   myLastCreatedElems.Clear();
4749   myLastCreatedNodes.Clear();
4750
4751   int aNbE;
4752   std::list<double> aPrms;
4753   TIDSortedElemSet::iterator itElem;
4754
4755   gp_XYZ aGC;
4756   TopoDS_Edge aTrackEdge;
4757   TopoDS_Vertex aV1, aV2;
4758
4759   SMDS_ElemIteratorPtr aItE;
4760   SMDS_NodeIteratorPtr aItN;
4761   SMDSAbs_ElementType aTypeE;
4762
4763   TNodeOfNodeListMap mapNewNodes;
4764
4765   // 1. Check data
4766   aNbE = theElements.size();
4767   // nothing to do
4768   if ( !aNbE )
4769     return EXTR_NO_ELEMENTS;
4770
4771   // 1.1 Track Pattern
4772   ASSERT( theTrack );
4773
4774   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4775
4776   aItE = pSubMeshDS->GetElements();
4777   while ( aItE->more() ) {
4778     const SMDS_MeshElement* pE = aItE->next();
4779     aTypeE = pE->GetType();
4780     // Pattern must contain links only
4781     if ( aTypeE != SMDSAbs_Edge )
4782       return EXTR_PATH_NOT_EDGE;
4783   }
4784
4785   list<SMESH_MeshEditor_PathPoint> fullList;
4786
4787   const TopoDS_Shape& aS = theTrack->GetSubShape();
4788   // Sub-shape for the Pattern must be an Edge or Wire
4789   if( aS.ShapeType() == TopAbs_EDGE ) {
4790     aTrackEdge = TopoDS::Edge( aS );
4791     // the Edge must not be degenerated
4792     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4793       return EXTR_BAD_PATH_SHAPE;
4794     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4795     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4796     const SMDS_MeshNode* aN1 = aItN->next();
4797     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4798     const SMDS_MeshNode* aN2 = aItN->next();
4799     // starting node must be aN1 or aN2
4800     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4801       return EXTR_BAD_STARTING_NODE;
4802     aItN = pSubMeshDS->GetNodes();
4803     while ( aItN->more() ) {
4804       const SMDS_MeshNode* pNode = aItN->next();
4805       const SMDS_EdgePosition* pEPos =
4806         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4807       double aT = pEPos->GetUParameter();
4808       aPrms.push_back( aT );
4809     }
4810     //Extrusion_Error err =
4811     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4812   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4813     list< SMESH_subMesh* > LSM;
4814     TopTools_SequenceOfShape Edges;
4815     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4816     while(itSM->more()) {
4817       SMESH_subMesh* SM = itSM->next();
4818       LSM.push_back(SM);
4819       const TopoDS_Shape& aS = SM->GetSubShape();
4820       Edges.Append(aS);
4821     }
4822     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4823     int startNid = theN1->GetID();
4824     TColStd_MapOfInteger UsedNums;
4825     
4826     int NbEdges = Edges.Length();
4827     int i = 1;
4828     for(; i<=NbEdges; i++) {
4829       int k = 0;
4830       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4831       for(; itLSM!=LSM.end(); itLSM++) {
4832         k++;
4833         if(UsedNums.Contains(k)) continue;
4834         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4835         SMESH_subMesh* locTrack = *itLSM;
4836         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4837         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4838         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4839         const SMDS_MeshNode* aN1 = aItN->next();
4840         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4841         const SMDS_MeshNode* aN2 = aItN->next();
4842         // starting node must be aN1 or aN2
4843         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4844         // 2. Collect parameters on the track edge
4845         aPrms.clear();
4846         aItN = locMeshDS->GetNodes();
4847         while ( aItN->more() ) {
4848           const SMDS_MeshNode* pNode = aItN->next();
4849           const SMDS_EdgePosition* pEPos =
4850             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4851           double aT = pEPos->GetUParameter();
4852           aPrms.push_back( aT );
4853         }
4854         list<SMESH_MeshEditor_PathPoint> LPP;
4855         //Extrusion_Error err =
4856         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4857         LLPPs.push_back(LPP);
4858         UsedNums.Add(k);
4859         // update startN for search following egde
4860         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4861         else startNid = aN1->GetID();
4862         break;
4863       }
4864     }
4865     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4866     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4867     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4868     for(; itPP!=firstList.end(); itPP++) {
4869       fullList.push_back( *itPP );
4870     }
4871     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4872     fullList.pop_back();
4873     itLLPP++;
4874     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4875       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4876       itPP = currList.begin();
4877       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4878       gp_Dir D1 = PP1.Tangent();
4879       gp_Dir D2 = PP2.Tangent();
4880       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4881                            (D1.Z()+D2.Z())/2 ) );
4882       PP1.SetTangent(Dnew);
4883       fullList.push_back(PP1);
4884       itPP++;
4885       for(; itPP!=firstList.end(); itPP++) {
4886         fullList.push_back( *itPP );
4887       }
4888       PP1 = fullList.back();
4889       fullList.pop_back();
4890     }
4891     // if wire not closed
4892     fullList.push_back(PP1);
4893     // else ???
4894   }
4895   else {
4896     return EXTR_BAD_PATH_SHAPE;
4897   }
4898
4899   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4900                           theHasRefPoint, theRefPoint, theMakeGroups);
4901 }
4902
4903
4904 //=======================================================================
4905 //function : ExtrusionAlongTrack
4906 //purpose  :
4907 //=======================================================================
4908 SMESH_MeshEditor::Extrusion_Error
4909 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4910                                        SMESH_Mesh*          theTrack,
4911                                        const SMDS_MeshNode* theN1,
4912                                        const bool           theHasAngles,
4913                                        list<double>&        theAngles,
4914                                        const bool           theLinearVariation,
4915                                        const bool           theHasRefPoint,
4916                                        const gp_Pnt&        theRefPoint,
4917                                        const bool           theMakeGroups)
4918 {
4919   myLastCreatedElems.Clear();
4920   myLastCreatedNodes.Clear();
4921
4922   int aNbE;
4923   std::list<double> aPrms;
4924   TIDSortedElemSet::iterator itElem;
4925
4926   gp_XYZ aGC;
4927   TopoDS_Edge aTrackEdge;
4928   TopoDS_Vertex aV1, aV2;
4929
4930   SMDS_ElemIteratorPtr aItE;
4931   SMDS_NodeIteratorPtr aItN;
4932   SMDSAbs_ElementType aTypeE;
4933
4934   TNodeOfNodeListMap mapNewNodes;
4935
4936   // 1. Check data
4937   aNbE = theElements.size();
4938   // nothing to do
4939   if ( !aNbE )
4940     return EXTR_NO_ELEMENTS;
4941
4942   // 1.1 Track Pattern
4943   ASSERT( theTrack );
4944
4945   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4946
4947   aItE = pMeshDS->elementsIterator();
4948   while ( aItE->more() ) {
4949     const SMDS_MeshElement* pE = aItE->next();
4950     aTypeE = pE->GetType();
4951     // Pattern must contain links only
4952     if ( aTypeE != SMDSAbs_Edge )
4953       return EXTR_PATH_NOT_EDGE;
4954   }
4955
4956   list<SMESH_MeshEditor_PathPoint> fullList;
4957
4958   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4959
4960   if( aS == SMESH_Mesh::PseudoShape() ) {
4961     //Mesh without shape
4962     const SMDS_MeshNode* currentNode = NULL;
4963     const SMDS_MeshNode* prevNode = theN1;
4964     std::vector<const SMDS_MeshNode*> aNodesList;
4965     aNodesList.push_back(theN1);
4966     int nbEdges = 0, conn=0;
4967     const SMDS_MeshElement* prevElem = NULL;
4968     const SMDS_MeshElement* currentElem = NULL;
4969     int totalNbEdges = theTrack->NbEdges();
4970     SMDS_ElemIteratorPtr nIt;
4971
4972     //check start node
4973     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
4974       return EXTR_BAD_STARTING_NODE;
4975     }
4976     
4977     conn = nbEdgeConnectivity(theN1);
4978     if(conn > 2)
4979       return EXTR_PATH_NOT_EDGE;
4980
4981     aItE = theN1->GetInverseElementIterator();
4982     prevElem = aItE->next();
4983     currentElem = prevElem;
4984     //Get all nodes
4985     if(totalNbEdges == 1 ) {
4986       nIt = currentElem->nodesIterator();
4987       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4988       if(currentNode == prevNode)
4989         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4990       aNodesList.push_back(currentNode);
4991     } else { 
4992       nIt = currentElem->nodesIterator();
4993       while( nIt->more() ) {
4994         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4995         if(currentNode == prevNode)
4996           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4997         aNodesList.push_back(currentNode);
4998         
4999         //case of the closed mesh
5000         if(currentNode == theN1) {
5001           nbEdges++;
5002           break;
5003         }
5004
5005         conn = nbEdgeConnectivity(currentNode);
5006         if(conn > 2) {
5007           return EXTR_PATH_NOT_EDGE;    
5008         }else if( conn == 1 && nbEdges > 0 ) {
5009           //End of the path
5010           nbEdges++;
5011           break;
5012         }else {
5013           prevNode = currentNode;
5014           aItE = currentNode->GetInverseElementIterator();
5015           currentElem = aItE->next();
5016           if( currentElem  == prevElem)
5017             currentElem = aItE->next();
5018           nIt = currentElem->nodesIterator();
5019           prevElem = currentElem;
5020           nbEdges++;
5021         }
5022       }
5023     } 
5024     
5025     if(nbEdges != totalNbEdges)
5026       return EXTR_PATH_NOT_EDGE;
5027
5028     TopTools_SequenceOfShape Edges;
5029     double x1,x2,y1,y2,z1,z2;
5030     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5031     int startNid = theN1->GetID();
5032     for(int i = 1; i < aNodesList.size(); i++) {
5033       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5034       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5035       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5036       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));  
5037       list<SMESH_MeshEditor_PathPoint> LPP;
5038       aPrms.clear();
5039       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5040       LLPPs.push_back(LPP);
5041       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5042       else startNid = aNodesList[i-1]->GetID();
5043
5044     }
5045
5046     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5047     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5048     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5049     for(; itPP!=firstList.end(); itPP++) {
5050       fullList.push_back( *itPP );
5051     }
5052
5053     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5054     SMESH_MeshEditor_PathPoint PP2;
5055     fullList.pop_back();
5056     itLLPP++;
5057     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5058       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5059       itPP = currList.begin();
5060       PP2 = currList.front();
5061       gp_Dir D1 = PP1.Tangent();
5062       gp_Dir D2 = PP2.Tangent();
5063       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5064                            (D1.Z()+D2.Z())/2 ) );
5065       PP1.SetTangent(Dnew);
5066       fullList.push_back(PP1);
5067       itPP++;
5068       for(; itPP!=currList.end(); itPP++) {
5069         fullList.push_back( *itPP );
5070       }
5071       PP1 = fullList.back();
5072       fullList.pop_back();
5073     }
5074     fullList.push_back(PP1);
5075     
5076   } // Sub-shape for the Pattern must be an Edge or Wire
5077   else if( aS.ShapeType() == TopAbs_EDGE ) {
5078     aTrackEdge = TopoDS::Edge( aS );
5079     // the Edge must not be degenerated
5080     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5081       return EXTR_BAD_PATH_SHAPE;
5082     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5083     aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5084     const SMDS_MeshNode* aN1 = aItN->next();
5085     aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5086     const SMDS_MeshNode* aN2 = aItN->next();
5087     // starting node must be aN1 or aN2
5088     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5089       return EXTR_BAD_STARTING_NODE;
5090     aItN = pMeshDS->nodesIterator();
5091     while ( aItN->more() ) {
5092       const SMDS_MeshNode* pNode = aItN->next();
5093       if( pNode==aN1 || pNode==aN2 ) continue;
5094       const SMDS_EdgePosition* pEPos =
5095         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5096       double aT = pEPos->GetUParameter();
5097       aPrms.push_back( aT );
5098     }
5099     //Extrusion_Error err =
5100     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5101   }
5102   else if( aS.ShapeType() == TopAbs_WIRE ) {
5103     list< SMESH_subMesh* > LSM;
5104     TopTools_SequenceOfShape Edges;
5105     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5106     for(; eExp.More(); eExp.Next()) {
5107       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5108       if( BRep_Tool::Degenerated(E) ) continue;
5109       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5110       if(SM) {
5111         LSM.push_back(SM);
5112         Edges.Append(E);
5113       }
5114     }
5115     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5116     int startNid = theN1->GetID();
5117     TColStd_MapOfInteger UsedNums;
5118     int NbEdges = Edges.Length();
5119     int i = 1;
5120     for(; i<=NbEdges; i++) {
5121       int k = 0;
5122       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5123       for(; itLSM!=LSM.end(); itLSM++) {
5124         k++;
5125         if(UsedNums.Contains(k)) continue;
5126         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5127         SMESH_subMesh* locTrack = *itLSM;
5128         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5129         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5130         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5131         const SMDS_MeshNode* aN1 = aItN->next();
5132         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5133         const SMDS_MeshNode* aN2 = aItN->next();
5134         // starting node must be aN1 or aN2
5135         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5136         // 2. Collect parameters on the track edge
5137         aPrms.clear();
5138         aItN = locMeshDS->GetNodes();
5139         while ( aItN->more() ) {
5140           const SMDS_MeshNode* pNode = aItN->next();
5141           const SMDS_EdgePosition* pEPos =
5142             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5143           double aT = pEPos->GetUParameter();
5144           aPrms.push_back( aT );
5145         }
5146         list<SMESH_MeshEditor_PathPoint> LPP;
5147         //Extrusion_Error err =
5148         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5149         LLPPs.push_back(LPP);
5150         UsedNums.Add(k);
5151         // update startN for search following egde
5152         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5153         else startNid = aN1->GetID();
5154         break;
5155       }
5156     }
5157     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5158     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5159     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5160     for(; itPP!=firstList.end(); itPP++) {
5161       fullList.push_back( *itPP );
5162     }
5163     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5164     fullList.pop_back();
5165     itLLPP++;
5166     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5167       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5168       itPP = currList.begin();
5169       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5170       gp_Dir D1 = PP1.Tangent();
5171       gp_Dir D2 = PP2.Tangent();
5172       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5173                            (D1.Z()+D2.Z())/2 ) );
5174       PP1.SetTangent(Dnew);
5175       fullList.push_back(PP1);
5176       itPP++;
5177       for(; itPP!=currList.end(); itPP++) {
5178         fullList.push_back( *itPP );
5179       }
5180       PP1 = fullList.back();
5181       fullList.pop_back();
5182     }
5183     // if wire not closed
5184     fullList.push_back(PP1);
5185     // else ???
5186   }
5187   else {
5188     return EXTR_BAD_PATH_SHAPE;
5189   }
5190
5191   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5192                           theHasRefPoint, theRefPoint, theMakeGroups);
5193 }
5194
5195
5196 //=======================================================================
5197 //function : MakeEdgePathPoints
5198 //purpose  : auxilary for ExtrusionAlongTrack
5199 //=======================================================================
5200 SMESH_MeshEditor::Extrusion_Error
5201 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5202                                      const TopoDS_Edge& aTrackEdge,
5203                                      bool FirstIsStart,
5204                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5205 {
5206   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5207   aTolVec=1.e-7;
5208   aTolVec2=aTolVec*aTolVec;
5209   double aT1, aT2;
5210   TopoDS_Vertex aV1, aV2;
5211   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5212   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5213   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5214   // 2. Collect parameters on the track edge
5215   aPrms.push_front( aT1 );
5216   aPrms.push_back( aT2 );
5217   // sort parameters
5218   aPrms.sort();
5219   if( FirstIsStart ) {
5220     if ( aT1 > aT2 ) {
5221       aPrms.reverse();
5222     }
5223   }
5224   else {
5225     if ( aT2 > aT1 ) {
5226       aPrms.reverse();
5227     }
5228   }
5229   // 3. Path Points
5230   SMESH_MeshEditor_PathPoint aPP;
5231   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5232   std::list<double>::iterator aItD = aPrms.begin();
5233   for(; aItD != aPrms.end(); ++aItD) {
5234     double aT = *aItD;
5235     gp_Pnt aP3D;
5236     gp_Vec aVec;
5237     aC3D->D1( aT, aP3D, aVec );
5238     aL2 = aVec.SquareMagnitude();
5239     if ( aL2 < aTolVec2 )
5240       return EXTR_CANT_GET_TANGENT;
5241     gp_Dir aTgt( aVec );
5242     aPP.SetPnt( aP3D );
5243     aPP.SetTangent( aTgt );
5244     aPP.SetParameter( aT );
5245     LPP.push_back(aPP);
5246   }
5247   return EXTR_OK;
5248 }
5249
5250
5251 //=======================================================================
5252 //function : MakeExtrElements
5253 //purpose  : auxilary for ExtrusionAlongTrack
5254 //=======================================================================
5255 SMESH_MeshEditor::Extrusion_Error
5256 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5257                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5258                                    const bool theHasAngles,
5259                                    list<double>& theAngles,
5260                                    const bool theLinearVariation,
5261                                    const bool theHasRefPoint,
5262                                    const gp_Pnt& theRefPoint,
5263                                    const bool theMakeGroups)
5264 {
5265   MESSAGE("MakeExtrElements");
5266   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5267   int aNbTP = fullList.size();
5268   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5269   // Angles
5270   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5271     LinearAngleVariation(aNbTP-1, theAngles);
5272   }
5273   vector<double> aAngles( aNbTP );
5274   int j = 0;
5275   for(; j<aNbTP; ++j) {
5276     aAngles[j] = 0.;
5277   }
5278   if ( theHasAngles ) {
5279     double anAngle;;
5280     std::list<double>::iterator aItD = theAngles.begin();
5281     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5282       anAngle = *aItD;
5283       aAngles[j] = anAngle;
5284     }
5285   }
5286   // fill vector of path points with angles
5287   //aPPs.resize(fullList.size());
5288   j = -1;
5289   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5290   for(; itPP!=fullList.end(); itPP++) {
5291     j++;
5292     SMESH_MeshEditor_PathPoint PP = *itPP;
5293     PP.SetAngle(aAngles[j]);
5294     aPPs[j] = PP;
5295   }
5296
5297   TNodeOfNodeListMap mapNewNodes;
5298   TElemOfVecOfNnlmiMap mapElemNewNodes;
5299   TElemOfElemListMap newElemsMap;
5300   TIDSortedElemSet::iterator itElem;
5301   double aX, aY, aZ;
5302   int aNb;
5303   SMDSAbs_ElementType aTypeE;
5304   // source elements for each generated one
5305   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5306
5307   // 3. Center of rotation aV0
5308   gp_Pnt aV0 = theRefPoint;
5309   gp_XYZ aGC;
5310   if ( !theHasRefPoint ) {
5311     aNb = 0;
5312     aGC.SetCoord( 0.,0.,0. );
5313
5314     itElem = theElements.begin();
5315     for ( ; itElem != theElements.end(); itElem++ ) {
5316       const SMDS_MeshElement* elem = *itElem;
5317
5318       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5319       while ( itN->more() ) {
5320         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5321         aX = node->X();
5322         aY = node->Y();
5323         aZ = node->Z();
5324
5325         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5326           list<const SMDS_MeshNode*> aLNx;
5327           mapNewNodes[node] = aLNx;
5328           //
5329           gp_XYZ aXYZ( aX, aY, aZ );
5330           aGC += aXYZ;
5331           ++aNb;
5332         }
5333       }
5334     }
5335     aGC /= aNb;
5336     aV0.SetXYZ( aGC );
5337   } // if (!theHasRefPoint) {
5338   mapNewNodes.clear();
5339
5340   // 4. Processing the elements
5341   SMESHDS_Mesh* aMesh = GetMeshDS();
5342
5343   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5344     // check element type
5345     const SMDS_MeshElement* elem = *itElem;
5346     aTypeE = elem->GetType();
5347     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5348       continue;
5349
5350     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5351     newNodesItVec.reserve( elem->NbNodes() );
5352
5353     // loop on elem nodes
5354     int nodeIndex = -1;
5355     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5356     while ( itN->more() )
5357     {
5358       ++nodeIndex;
5359       // check if a node has been already processed
5360       const SMDS_MeshNode* node =
5361         static_cast<const SMDS_MeshNode*>( itN->next() );
5362       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5363       if ( nIt == mapNewNodes.end() ) {
5364         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5365         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5366
5367         // make new nodes
5368         aX = node->X();  aY = node->Y(); aZ = node->Z();
5369
5370         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5371         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5372         gp_Ax1 anAx1, anAxT1T0;
5373         gp_Dir aDT1x, aDT0x, aDT1T0;
5374
5375         aTolAng=1.e-4;
5376
5377         aV0x = aV0;
5378         aPN0.SetCoord(aX, aY, aZ);
5379
5380         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5381         aP0x = aPP0.Pnt();
5382         aDT0x= aPP0.Tangent();
5383         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5384
5385         for ( j = 1; j < aNbTP; ++j ) {
5386           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5387           aP1x = aPP1.Pnt();
5388           aDT1x = aPP1.Tangent();
5389           aAngle1x = aPP1.Angle();
5390
5391           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5392           // Translation
5393           gp_Vec aV01x( aP0x, aP1x );
5394           aTrsf.SetTranslation( aV01x );
5395
5396           // traslated point
5397           aV1x = aV0x.Transformed( aTrsf );
5398           aPN1 = aPN0.Transformed( aTrsf );
5399
5400           // rotation 1 [ T1,T0 ]
5401           aAngleT1T0=-aDT1x.Angle( aDT0x );
5402           if (fabs(aAngleT1T0) > aTolAng) {
5403             aDT1T0=aDT1x^aDT0x;
5404             anAxT1T0.SetLocation( aV1x );
5405             anAxT1T0.SetDirection( aDT1T0 );
5406             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5407
5408             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5409           }
5410
5411           // rotation 2
5412           if ( theHasAngles ) {
5413             anAx1.SetLocation( aV1x );
5414             anAx1.SetDirection( aDT1x );
5415             aTrsfRot.SetRotation( anAx1, aAngle1x );
5416
5417             aPN1 = aPN1.Transformed( aTrsfRot );
5418           }
5419
5420           // make new node
5421           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5422           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5423             // create additional node
5424             double x = ( aPN1.X() + aPN0.X() )/2.;
5425             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5426             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5427             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5428             myLastCreatedNodes.Append(newNode);
5429             srcNodes.Append( node );
5430             listNewNodes.push_back( newNode );
5431           }
5432           aX = aPN1.X();
5433           aY = aPN1.Y();
5434           aZ = aPN1.Z();
5435           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5436           myLastCreatedNodes.Append(newNode);
5437           srcNodes.Append( node );
5438           listNewNodes.push_back( newNode );
5439
5440           aPN0 = aPN1;
5441           aP0x = aP1x;
5442           aV0x = aV1x;
5443           aDT0x = aDT1x;
5444         }
5445       }
5446
5447       else {
5448         // if current elem is quadratic and current node is not medium
5449         // we have to check - may be it is needed to insert additional nodes
5450         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5451           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5452           if(listNewNodes.size()==aNbTP-1) {
5453             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5454             gp_XYZ P(node->X(), node->Y(), node->Z());
5455             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5456             int i;
5457             for(i=0; i<aNbTP-1; i++) {
5458               const SMDS_MeshNode* N = *it;
5459               double x = ( N->X() + P.X() )/2.;
5460               double y = ( N->Y() + P.Y() )/2.;
5461               double z = ( N->Z() + P.Z() )/2.;
5462               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5463               srcNodes.Append( node );
5464               myLastCreatedNodes.Append(newN);
5465               aNodes[2*i] = newN;
5466               aNodes[2*i+1] = N;
5467               P = gp_XYZ(N->X(),N->Y(),N->Z());
5468             }
5469             listNewNodes.clear();
5470             for(i=0; i<2*(aNbTP-1); i++) {
5471               listNewNodes.push_back(aNodes[i]);
5472             }
5473           }
5474         }
5475       }
5476
5477       newNodesItVec.push_back( nIt );
5478     }
5479     // make new elements
5480     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5481     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5482     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5483   }
5484
5485   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5486
5487   if ( theMakeGroups )
5488     generateGroups( srcNodes, srcElems, "extruded");
5489
5490   return EXTR_OK;
5491 }
5492
5493
5494 //=======================================================================
5495 //function : LinearAngleVariation
5496 //purpose  : auxilary for ExtrusionAlongTrack
5497 //=======================================================================
5498 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5499                                             list<double>& Angles)
5500 {
5501   int nbAngles = Angles.size();
5502   if( nbSteps > nbAngles ) {
5503     vector<double> theAngles(nbAngles);
5504     list<double>::iterator it = Angles.begin();
5505     int i = -1;
5506     for(; it!=Angles.end(); it++) {
5507       i++;
5508       theAngles[i] = (*it);
5509     }
5510     list<double> res;
5511     double rAn2St = double( nbAngles ) / double( nbSteps );
5512     double angPrev = 0, angle;
5513     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5514       double angCur = rAn2St * ( iSt+1 );
5515       double angCurFloor  = floor( angCur );
5516       double angPrevFloor = floor( angPrev );
5517       if ( angPrevFloor == angCurFloor )
5518         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5519       else {
5520         int iP = int( angPrevFloor );
5521         double angPrevCeil = ceil(angPrev);
5522         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5523
5524         int iC = int( angCurFloor );
5525         if ( iC < nbAngles )
5526           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5527
5528         iP = int( angPrevCeil );
5529         while ( iC-- > iP )
5530           angle += theAngles[ iC ];
5531       }
5532       res.push_back(angle);
5533       angPrev = angCur;
5534     }
5535     Angles.clear();
5536     it = res.begin();
5537     for(; it!=res.end(); it++)
5538       Angles.push_back( *it );
5539   }
5540 }
5541
5542
5543 //================================================================================
5544 /*!
5545  * \brief Move or copy theElements applying theTrsf to their nodes
5546  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5547  *  \param theTrsf - transformation to apply
5548  *  \param theCopy - if true, create translated copies of theElems
5549  *  \param theMakeGroups - if true and theCopy, create translated groups
5550  *  \param theTargetMesh - mesh to copy translated elements into
5551  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5552  */
5553 //================================================================================
5554
5555 SMESH_MeshEditor::PGroupIDs
5556 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5557                              const gp_Trsf&     theTrsf,
5558                              const bool         theCopy,
5559                              const bool         theMakeGroups,
5560                              SMESH_Mesh*        theTargetMesh)
5561 {
5562   myLastCreatedElems.Clear();
5563   myLastCreatedNodes.Clear();
5564
5565   bool needReverse = false;
5566   string groupPostfix;
5567   switch ( theTrsf.Form() ) {
5568   case gp_PntMirror:
5569     MESSAGE("gp_PntMirror");
5570     needReverse = true;
5571     groupPostfix = "mirrored";
5572     break;
5573   case gp_Ax1Mirror:
5574     MESSAGE("gp_Ax1Mirror");
5575     groupPostfix = "mirrored";
5576     break;
5577   case gp_Ax2Mirror:
5578     MESSAGE("gp_Ax2Mirror");
5579     needReverse = true;
5580     groupPostfix = "mirrored";
5581     break;
5582   case gp_Rotation:
5583     MESSAGE("gp_Rotation");
5584     groupPostfix = "rotated";
5585     break;
5586   case gp_Translation:
5587     MESSAGE("gp_Translation");
5588     groupPostfix = "translated";
5589     break;
5590   case gp_Scale:
5591     MESSAGE("gp_Scale");
5592     groupPostfix = "scaled";
5593     break;
5594   case gp_CompoundTrsf: // different scale by axis
5595     MESSAGE("gp_CompoundTrsf");
5596     groupPostfix = "scaled";
5597     break;
5598   default:
5599     MESSAGE("default");
5600     needReverse = false;
5601     groupPostfix = "transformed";
5602   }
5603
5604   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5605   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5606   SMESHDS_Mesh* aMesh    = GetMeshDS();
5607
5608
5609   // map old node to new one
5610   TNodeNodeMap nodeMap;
5611
5612   // elements sharing moved nodes; those of them which have all
5613   // nodes mirrored but are not in theElems are to be reversed
5614   TIDSortedElemSet inverseElemSet;
5615
5616   // source elements for each generated one
5617   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5618
5619   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5620   TIDSortedElemSet orphanNode;
5621
5622   if ( theElems.empty() ) // transform the whole mesh
5623   {
5624     // add all elements
5625     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5626     while ( eIt->more() ) theElems.insert( eIt->next() );
5627     // add orphan nodes
5628     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5629     while ( nIt->more() )
5630     {
5631       const SMDS_MeshNode* node = nIt->next();
5632       if ( node->NbInverseElements() == 0)
5633         orphanNode.insert( node );
5634     }
5635   }
5636
5637   // loop on elements to transform nodes : first orphan nodes then elems
5638   TIDSortedElemSet::iterator itElem;
5639   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5640   for (int i=0; i<2; i++)
5641   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5642     const SMDS_MeshElement* elem = *itElem;
5643     if ( !elem )
5644       continue;
5645
5646     // loop on elem nodes
5647     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5648     while ( itN->more() ) {
5649
5650       const SMDS_MeshNode* node = cast2Node( itN->next() );
5651       // check if a node has been already transformed
5652       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5653         nodeMap.insert( make_pair ( node, node ));
5654       if ( !n2n_isnew.second )
5655         continue;
5656
5657       double coord[3];
5658       coord[0] = node->X();
5659       coord[1] = node->Y();
5660       coord[2] = node->Z();
5661       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5662       if ( theTargetMesh ) {
5663         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5664         n2n_isnew.first->second = newNode;
5665         myLastCreatedNodes.Append(newNode);
5666         srcNodes.Append( node );
5667       }
5668       else if ( theCopy ) {
5669         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5670         n2n_isnew.first->second = newNode;
5671         myLastCreatedNodes.Append(newNode);
5672         srcNodes.Append( node );
5673       }
5674       else {
5675         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5676         // node position on shape becomes invalid
5677         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5678           ( SMDS_SpacePosition::originSpacePosition() );
5679       }
5680
5681       // keep inverse elements
5682       if ( !theCopy && !theTargetMesh && needReverse ) {
5683         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5684         while ( invElemIt->more() ) {
5685           const SMDS_MeshElement* iel = invElemIt->next();
5686           inverseElemSet.insert( iel );
5687         }
5688       }
5689     }
5690   }
5691
5692   // either create new elements or reverse mirrored ones
5693   if ( !theCopy && !needReverse && !theTargetMesh )
5694     return PGroupIDs();
5695
5696   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5697   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5698     theElems.insert( *invElemIt );
5699
5700   // Replicate or reverse elements
5701
5702   std::vector<int> iForw;
5703   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5704   {
5705     const SMDS_MeshElement* elem = *itElem;
5706     if ( !elem ) continue;
5707
5708     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5709     int                  nbNodes  = elem->NbNodes();
5710     if ( geomType == SMDSGeom_POINT ) continue; // node
5711
5712     switch ( geomType ) {
5713
5714     case SMDSGeom_POLYGON:  // ---------------------- polygon
5715       {
5716         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5717         int iNode = 0;
5718         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5719         while (itN->more()) {
5720           const SMDS_MeshNode* node =
5721             static_cast<const SMDS_MeshNode*>(itN->next());
5722           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5723           if (nodeMapIt == nodeMap.end())
5724             break; // not all nodes transformed
5725           if (needReverse) {
5726             // reverse mirrored faces and volumes
5727             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5728           } else {
5729             poly_nodes[iNode] = (*nodeMapIt).second;
5730           }
5731           iNode++;
5732         }
5733         if ( iNode != nbNodes )
5734           continue; // not all nodes transformed
5735
5736         if ( theTargetMesh ) {
5737           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5738           srcElems.Append( elem );
5739         }
5740         else if ( theCopy ) {
5741           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5742           srcElems.Append( elem );
5743         }
5744         else {
5745           aMesh->ChangePolygonNodes(elem, poly_nodes);
5746         }
5747       }
5748       break;
5749
5750     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5751       {
5752         const SMDS_VtkVolume* aPolyedre =
5753           dynamic_cast<const SMDS_VtkVolume*>( elem );
5754         if (!aPolyedre) {
5755           MESSAGE("Warning: bad volumic element");
5756           continue;
5757         }
5758
5759         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5760         vector<int> quantities; quantities.reserve( nbNodes );
5761
5762         bool allTransformed = true;
5763         int nbFaces = aPolyedre->NbFaces();
5764         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5765           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5766           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5767             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5768             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5769             if (nodeMapIt == nodeMap.end()) {
5770               allTransformed = false; // not all nodes transformed
5771             } else {
5772               poly_nodes.push_back((*nodeMapIt).second);
5773             }
5774             if ( needReverse && allTransformed )
5775               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5776           }
5777           quantities.push_back(nbFaceNodes);
5778         }
5779         if ( !allTransformed )
5780           continue; // not all nodes transformed
5781
5782         if ( theTargetMesh ) {
5783           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5784           srcElems.Append( elem );
5785         }
5786         else if ( theCopy ) {
5787           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5788           srcElems.Append( elem );
5789         }
5790         else {
5791           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5792         }
5793       }
5794       break;
5795
5796     case SMDSGeom_BALL: // -------------------- Ball
5797       {
5798         if ( !theCopy && !theTargetMesh ) continue;
5799
5800         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5801         if (nodeMapIt == nodeMap.end())
5802           continue; // not all nodes transformed
5803
5804         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5805         if ( theTargetMesh ) {
5806           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5807           srcElems.Append( elem );
5808         }
5809         else {
5810           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5811           srcElems.Append( elem );
5812         }
5813       }
5814       break;
5815
5816     default: // ----------------------- Regular elements
5817
5818       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5819       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5820       const std::vector<int>& i = needReverse ? iRev : iForw;
5821
5822       // find transformed nodes
5823       vector<const SMDS_MeshNode*> nodes(nbNodes);
5824       int iNode = 0;
5825       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5826       while ( itN->more() ) {
5827         const SMDS_MeshNode* node =
5828           static_cast<const SMDS_MeshNode*>( itN->next() );
5829         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5830         if ( nodeMapIt == nodeMap.end() )
5831           break; // not all nodes transformed
5832         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5833       }
5834       if ( iNode != nbNodes )
5835         continue; // not all nodes transformed
5836
5837       if ( theTargetMesh ) {
5838         if ( SMDS_MeshElement* copy =
5839              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5840           myLastCreatedElems.Append( copy );
5841           srcElems.Append( elem );
5842         }
5843       }
5844       else if ( theCopy ) {
5845         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5846           srcElems.Append( elem );
5847       }
5848       else {
5849         // reverse element as it was reversed by transformation
5850         if ( nbNodes > 2 )
5851           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5852       }
5853     } // switch ( geomType )
5854
5855   } // loop on elements
5856
5857   PGroupIDs newGroupIDs;
5858
5859   if ( ( theMakeGroups && theCopy ) ||
5860        ( theMakeGroups && theTargetMesh ) )
5861     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5862
5863   return newGroupIDs;
5864 }
5865
5866 //=======================================================================
5867 /*!
5868  * \brief Create groups of elements made during transformation
5869  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5870  * \param elemGens - elements making corresponding myLastCreatedElems
5871  * \param postfix - to append to names of new groups
5872  */
5873 //=======================================================================
5874
5875 SMESH_MeshEditor::PGroupIDs
5876 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5877                                  const SMESH_SequenceOfElemPtr& elemGens,
5878                                  const std::string&             postfix,
5879                                  SMESH_Mesh*                    targetMesh)
5880 {
5881   PGroupIDs newGroupIDs( new list<int> );
5882   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5883
5884   // Sort existing groups by types and collect their names
5885
5886   // to store an old group and a generated new one
5887   typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
5888   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5889   // group names
5890   set< string > groupNames;
5891   //
5892   SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
5893   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5894   while ( groupIt->more() ) {
5895     SMESH_Group * group = groupIt->next();
5896     if ( !group ) continue;
5897     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5898     if ( !groupDS || groupDS->IsEmpty() ) continue;
5899     groupNames.insert( group->GetName() );
5900     groupDS->SetStoreName( group->GetName() );
5901     groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
5902   }
5903
5904   // Groups creation
5905
5906   // loop on nodes and elements
5907   for ( int isNodes = 0; isNodes < 2; ++isNodes )
5908   {
5909     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
5910     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5911     if ( gens.Length() != elems.Length() )
5912       throw SALOME_Exception(LOCALIZED("invalid args"));
5913
5914     // loop on created elements
5915     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5916     {
5917       const SMDS_MeshElement* sourceElem = gens( iElem );
5918       if ( !sourceElem ) {
5919         MESSAGE("generateGroups(): NULL source element");
5920         continue;
5921       }
5922       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5923       if ( groupsOldNew.empty() ) {
5924         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5925           ++iElem; // skip all elements made by sourceElem
5926         continue;
5927       }
5928       // collect all elements made by sourceElem
5929       list< const SMDS_MeshElement* > resultElems;
5930       if ( const SMDS_MeshElement* resElem = elems( iElem ))
5931         if ( resElem != sourceElem )
5932           resultElems.push_back( resElem );
5933       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5934         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5935           if ( resElem != sourceElem )
5936             resultElems.push_back( resElem );
5937       // do not generate element groups from node ones
5938 //      if ( sourceElem->GetType() == SMDSAbs_Node &&
5939 //           elems( iElem )->GetType() != SMDSAbs_Node )
5940 //        continue;
5941
5942       // add resultElems to groups made by ones the sourceElem belongs to
5943       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5944       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5945       {
5946         SMESHDS_GroupBase* oldGroup = gOldNew->first;
5947         if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
5948         {
5949           SMDS_MeshGroup* & newGroup = gOldNew->second;
5950           if ( !newGroup )// create a new group
5951           {
5952             // make a name
5953             string name = oldGroup->GetStoreName();
5954             if ( !targetMesh ) {
5955               name += "_";
5956               name += postfix;
5957               int nb = 0;
5958               while ( !groupNames.insert( name ).second ) // name exists
5959               {
5960                 if ( nb == 0 ) {
5961                   name += "_1";
5962                 }
5963                 else {
5964                   TCollection_AsciiString nbStr(nb+1);
5965                   name.resize( name.rfind('_')+1 );
5966                   name += nbStr.ToCString();
5967                 }
5968                 ++nb;
5969               }
5970             }
5971             // make a group
5972             int id;
5973             SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
5974                                                  name.c_str(), id );
5975             SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
5976             newGroup = & groupDS->SMDSGroup();
5977             newGroupIDs->push_back( id );
5978           }
5979
5980           // fill in a new group
5981           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5982           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5983             newGroup->Add( *resElemIt );
5984         }
5985       }
5986     } // loop on created elements
5987   }// loop on nodes and elements
5988
5989   return newGroupIDs;
5990 }
5991
5992 //================================================================================
5993 /*!
5994  * \brief Return list of group of nodes close to each other within theTolerance
5995  *        Search among theNodes or in the whole mesh if theNodes is empty using
5996  *        an Octree algorithm
5997  */
5998 //================================================================================
5999
6000 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6001                                             const double         theTolerance,
6002                                             TListOfListOfNodes & theGroupsOfNodes)
6003 {
6004   myLastCreatedElems.Clear();
6005   myLastCreatedNodes.Clear();
6006
6007   if ( theNodes.empty() )
6008   { // get all nodes in the mesh
6009     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6010     while ( nIt->more() )
6011       theNodes.insert( theNodes.end(),nIt->next());
6012   }
6013
6014   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6015 }
6016
6017
6018 //=======================================================================
6019 /*!
6020  * \brief Implementation of search for the node closest to point
6021  */
6022 //=======================================================================
6023
6024 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6025 {
6026   //---------------------------------------------------------------------
6027   /*!
6028    * \brief Constructor
6029    */
6030   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6031   {
6032     myMesh = ( SMESHDS_Mesh* ) theMesh;
6033
6034     TIDSortedNodeSet nodes;
6035     if ( theMesh ) {
6036       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6037       while ( nIt->more() )
6038         nodes.insert( nodes.end(), nIt->next() );
6039     }
6040     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6041
6042     // get max size of a leaf box
6043     SMESH_OctreeNode* tree = myOctreeNode;
6044     while ( !tree->isLeaf() )
6045     {
6046       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6047       if ( cIt->more() )
6048         tree = cIt->next();
6049     }
6050     myHalfLeafSize = tree->maxSize() / 2.;
6051   }
6052
6053   //---------------------------------------------------------------------
6054   /*!
6055    * \brief Move node and update myOctreeNode accordingly
6056    */
6057   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6058   {
6059     myOctreeNode->UpdateByMoveNode( node, toPnt );
6060     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6061   }
6062
6063   //---------------------------------------------------------------------
6064   /*!
6065    * \brief Do it's job
6066    */
6067   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6068   {
6069     map<double, const SMDS_MeshNode*> dist2Nodes;
6070     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6071     if ( !dist2Nodes.empty() )
6072       return dist2Nodes.begin()->second;
6073     list<const SMDS_MeshNode*> nodes;
6074     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6075
6076     double minSqDist = DBL_MAX;
6077     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6078     {
6079       // sort leafs by their distance from thePnt
6080       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6081       TDistTreeMap treeMap;
6082       list< SMESH_OctreeNode* > treeList;
6083       list< SMESH_OctreeNode* >::iterator trIt;
6084       treeList.push_back( myOctreeNode );
6085
6086       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6087       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6088       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6089       {
6090         SMESH_OctreeNode* tree = *trIt;
6091         if ( !tree->isLeaf() ) // put children to the queue
6092         {
6093           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6094           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6095           while ( cIt->more() )
6096             treeList.push_back( cIt->next() );
6097         }
6098         else if ( tree->NbNodes() ) // put a tree to the treeMap
6099         {
6100           const Bnd_B3d& box = tree->getBox();
6101           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6102           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6103           if ( !it_in.second ) // not unique distance to box center
6104             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6105         }
6106       }
6107       // find distance after which there is no sense to check tree's
6108       double sqLimit = DBL_MAX;
6109       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6110       if ( treeMap.size() > 5 ) {
6111         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6112         const Bnd_B3d& box = closestTree->getBox();
6113         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6114         sqLimit = limit * limit;
6115       }
6116       // get all nodes from trees
6117       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6118         if ( sqDist_tree->first > sqLimit )
6119           break;
6120         SMESH_OctreeNode* tree = sqDist_tree->second;
6121         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6122       }
6123     }
6124     // find closest among nodes
6125     minSqDist = DBL_MAX;
6126     const SMDS_MeshNode* closestNode = 0;
6127     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6128     for ( ; nIt != nodes.end(); ++nIt ) {
6129       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6130       if ( minSqDist > sqDist ) {
6131         closestNode = *nIt;
6132         minSqDist = sqDist;
6133       }
6134     }
6135     return closestNode;
6136   }
6137
6138   //---------------------------------------------------------------------
6139   /*!
6140    * \brief Destructor
6141    */
6142   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6143
6144   //---------------------------------------------------------------------
6145   /*!
6146    * \brief Return the node tree
6147    */
6148   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6149
6150 private:
6151   SMESH_OctreeNode* myOctreeNode;
6152   SMESHDS_Mesh*     myMesh;
6153   double            myHalfLeafSize; // max size of a leaf box
6154 };
6155
6156 //=======================================================================
6157 /*!
6158  * \brief Return SMESH_NodeSearcher
6159  */
6160 //=======================================================================
6161
6162 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher() 
6163 {
6164   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6165 }
6166
6167 // ========================================================================
6168 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6169 {
6170   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6171   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6172   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6173
6174   //=======================================================================
6175   /*!
6176    * \brief Octal tree of bounding boxes of elements
6177    */
6178   //=======================================================================
6179
6180   class ElementBndBoxTree : public SMESH_Octree
6181   {
6182   public:
6183
6184     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6185                       SMDSAbs_ElementType  elemType,
6186                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6187                       double               tolerance = NodeRadius );
6188     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6189     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6190     void getElementsInSphere ( const gp_XYZ& center,
6191                                const double  radius, TIDSortedElemSet& foundElems);
6192     size_t getSize() { return std::max( _size, _elements.size() ); }
6193     ~ElementBndBoxTree();
6194
6195   protected:
6196     ElementBndBoxTree():_size(0) {}
6197     SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6198     void          buildChildrenData();
6199     Bnd_B3d*      buildRootBox();
6200   private:
6201     //!< Bounding box of element
6202     struct ElementBox : public Bnd_B3d
6203     {
6204       const SMDS_MeshElement* _element;
6205       int                     _refCount; // an ElementBox can be included in several tree branches
6206       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6207     };
6208     vector< ElementBox* > _elements;
6209     size_t                _size;
6210   };
6211
6212   //================================================================================
6213   /*!
6214    * \brief ElementBndBoxTree creation
6215    */
6216   //================================================================================
6217
6218   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6219     :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6220   {
6221     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6222     _elements.reserve( nbElems );
6223
6224     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6225     while ( elemIt->more() )
6226       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6227
6228     compute();
6229   }
6230
6231   //================================================================================
6232   /*!
6233    * \brief Destructor
6234    */
6235   //================================================================================
6236
6237   ElementBndBoxTree::~ElementBndBoxTree()
6238   {
6239     for ( int i = 0; i < _elements.size(); ++i )
6240       if ( --_elements[i]->_refCount <= 0 )
6241         delete _elements[i];
6242   }
6243
6244   //================================================================================
6245   /*!
6246    * \brief Return the maximal box
6247    */
6248   //================================================================================
6249
6250   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6251   {
6252     Bnd_B3d* box = new Bnd_B3d;
6253     for ( int i = 0; i < _elements.size(); ++i )
6254       box->Add( *_elements[i] );
6255     return box;
6256   }
6257
6258   //================================================================================
6259   /*!
6260    * \brief Redistrubute element boxes among children
6261    */
6262   //================================================================================
6263
6264   void ElementBndBoxTree::buildChildrenData()
6265   {
6266     for ( int i = 0; i < _elements.size(); ++i )
6267     {
6268       for (int j = 0; j < 8; j++)
6269       {
6270         if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6271         {
6272           _elements[i]->_refCount++;
6273           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6274         }
6275       }
6276       _elements[i]->_refCount--;
6277     }
6278     _size = _elements.size();
6279     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6280
6281     for (int j = 0; j < 8; j++)
6282     {
6283       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6284       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6285         child->myIsLeaf = true;
6286
6287       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6288         SMESHUtils::CompactVector( child->_elements );
6289     }
6290   }
6291
6292   //================================================================================
6293   /*!
6294    * \brief Return elements which can include the point
6295    */
6296   //================================================================================
6297
6298   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6299                                                 TIDSortedElemSet& foundElems)
6300   {
6301     if ( getBox().IsOut( point.XYZ() ))
6302       return;
6303
6304     if ( isLeaf() )
6305     {
6306       for ( int i = 0; i < _elements.size(); ++i )
6307         if ( !_elements[i]->IsOut( point.XYZ() ))
6308           foundElems.insert( _elements[i]->_element );
6309     }
6310     else
6311     {
6312       for (int i = 0; i < 8; i++)
6313         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6314     }
6315   }
6316
6317   //================================================================================
6318   /*!
6319    * \brief Return elements which can be intersected by the line
6320    */
6321   //================================================================================
6322
6323   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6324                                                TIDSortedElemSet& foundElems)
6325   {
6326     if ( getBox().IsOut( line ))
6327       return;
6328
6329     if ( isLeaf() )
6330     {
6331       for ( int i = 0; i < _elements.size(); ++i )
6332         if ( !_elements[i]->IsOut( line ))
6333           foundElems.insert( _elements[i]->_element );
6334     }
6335     else
6336     {
6337       for (int i = 0; i < 8; i++)
6338         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6339     }
6340   }
6341
6342   //================================================================================
6343   /*!
6344    * \brief Return elements from leaves intersecting the sphere
6345    */
6346   //================================================================================
6347
6348   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6349                                                 const double      radius,
6350                                                 TIDSortedElemSet& foundElems)
6351   {
6352     if ( getBox().IsOut( center, radius ))
6353       return;
6354
6355     if ( isLeaf() )
6356     {
6357       for ( int i = 0; i < _elements.size(); ++i )
6358         if ( !_elements[i]->IsOut( center, radius ))
6359           foundElems.insert( _elements[i]->_element );
6360     }
6361     else
6362     {
6363       for (int i = 0; i < 8; i++)
6364         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6365     }
6366   }
6367
6368   //================================================================================
6369   /*!
6370    * \brief Construct the element box
6371    */
6372   //================================================================================
6373
6374   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6375   {
6376     _element  = elem;
6377     _refCount = 1;
6378     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6379     while ( nIt->more() )
6380       Add( SMESH_TNodeXYZ( nIt->next() ));
6381     Enlarge( tolerance );
6382   }
6383
6384 } // namespace
6385
6386 //=======================================================================
6387 /*!
6388  * \brief Implementation of search for the elements by point and
6389  *        of classification of point in 2D mesh
6390  */
6391 //=======================================================================
6392
6393 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6394 {
6395   SMESHDS_Mesh*                _mesh;
6396   SMDS_ElemIteratorPtr         _meshPartIt;
6397   ElementBndBoxTree*           _ebbTree;
6398   SMESH_NodeSearcherImpl*      _nodeSearcher;
6399   SMDSAbs_ElementType          _elementType;
6400   double                       _tolerance;
6401   bool                         _outerFacesFound;
6402   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6403
6404   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6405     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6406   ~SMESH_ElementSearcherImpl()
6407   {
6408     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6409     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6410   }
6411   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6412                                   SMDSAbs_ElementType                type,
6413                                   vector< const SMDS_MeshElement* >& foundElements);
6414   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6415   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6416                                                  SMDSAbs_ElementType type );
6417
6418   void GetElementsNearLine( const gp_Ax1&                      line,
6419                             SMDSAbs_ElementType                type,
6420                             vector< const SMDS_MeshElement* >& foundElems);
6421   double getTolerance();
6422   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6423                             const double tolerance, double & param);
6424   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6425   bool isOuterBoundary(const SMDS_MeshElement* face) const
6426   {
6427     return _outerFaces.empty() || _outerFaces.count(face);
6428   }
6429   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6430   {
6431     const SMDS_MeshElement* _face;
6432     gp_Vec                  _faceNorm;
6433     bool                    _coincides; //!< the line lays in face plane
6434     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6435       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6436   };
6437   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6438   {
6439     SMESH_TLink      _link;
6440     TIDSortedElemSet _faces;
6441     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6442       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6443   };
6444 };
6445
6446 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6447 {
6448   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6449              << ", _coincides="<<i._coincides << ")";
6450 }
6451
6452 //=======================================================================
6453 /*!
6454  * \brief define tolerance for search
6455  */
6456 //=======================================================================
6457
6458 double SMESH_ElementSearcherImpl::getTolerance()
6459 {
6460   if ( _tolerance < 0 )
6461   {
6462     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6463
6464     _tolerance = 0;
6465     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6466     {
6467       double boxSize = _nodeSearcher->getTree()->maxSize();
6468       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6469     }
6470     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6471     {
6472       double boxSize = _ebbTree->maxSize();
6473       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6474     }
6475     if ( _tolerance == 0 )
6476     {
6477       // define tolerance by size of a most complex element
6478       int complexType = SMDSAbs_Volume;
6479       while ( complexType > SMDSAbs_All &&
6480               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6481         --complexType;
6482       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6483       double elemSize;
6484       if ( complexType == int( SMDSAbs_Node ))
6485       {
6486         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6487         elemSize = 1;
6488         if ( meshInfo.NbNodes() > 2 )
6489           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6490       }
6491       else
6492       {
6493         SMDS_ElemIteratorPtr elemIt =
6494             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6495         const SMDS_MeshElement* elem = elemIt->next();
6496         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6497         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6498         elemSize = 0;
6499         while ( nodeIt->more() )
6500         {
6501           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6502           elemSize = max( dist, elemSize );
6503         }
6504       }
6505       _tolerance = 1e-4 * elemSize;
6506     }
6507   }
6508   return _tolerance;
6509 }
6510
6511 //================================================================================
6512 /*!
6513  * \brief Find intersection of the line and an edge of face and return parameter on line
6514  */
6515 //================================================================================
6516
6517 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6518                                                      const SMDS_MeshElement* face,
6519                                                      const double            tol,
6520                                                      double &                param)
6521 {
6522   int nbInts = 0;
6523   param = 0;
6524
6525   GeomAPI_ExtremaCurveCurve anExtCC;
6526   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6527   
6528   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6529   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6530   {
6531     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6532                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) )); 
6533     anExtCC.Init( lineCurve, edge);
6534     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6535     {
6536       Quantity_Parameter pl, pe;
6537       anExtCC.LowerDistanceParameters( pl, pe );
6538       param += pl;
6539       if ( ++nbInts == 2 )
6540         break;
6541     }
6542   }
6543   if ( nbInts > 0 ) param /= nbInts;
6544   return nbInts > 0;
6545 }
6546 //================================================================================
6547 /*!
6548  * \brief Find all faces belonging to the outer boundary of mesh
6549  */
6550 //================================================================================
6551
6552 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6553 {
6554   if ( _outerFacesFound ) return;
6555
6556   // Collect all outer faces by passing from one outer face to another via their links
6557   // and BTW find out if there are internal faces at all.
6558
6559   // checked links and links where outer boundary meets internal one
6560   set< SMESH_TLink > visitedLinks, seamLinks;
6561
6562   // links to treat with already visited faces sharing them
6563   list < TFaceLink > startLinks;
6564
6565   // load startLinks with the first outerFace
6566   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6567   _outerFaces.insert( outerFace );
6568
6569   TIDSortedElemSet emptySet;
6570   while ( !startLinks.empty() )
6571   {
6572     const SMESH_TLink& link  = startLinks.front()._link;
6573     TIDSortedElemSet&  faces = startLinks.front()._faces;
6574
6575     outerFace = *faces.begin();
6576     // find other faces sharing the link
6577     const SMDS_MeshElement* f;
6578     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6579       faces.insert( f );
6580
6581     // select another outer face among the found 
6582     const SMDS_MeshElement* outerFace2 = 0;
6583     if ( faces.size() == 2 )
6584     {
6585       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6586     }
6587     else if ( faces.size() > 2 )
6588     {
6589       seamLinks.insert( link );
6590
6591       // link direction within the outerFace
6592       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6593                    SMESH_TNodeXYZ( link.node2()));
6594       int i1 = outerFace->GetNodeIndex( link.node1() );
6595       int i2 = outerFace->GetNodeIndex( link.node2() );
6596       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6597       if ( rev ) n1n2.Reverse();
6598       // outerFace normal
6599       gp_XYZ ofNorm, fNorm;
6600       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6601       {
6602         // direction from the link inside outerFace
6603         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6604         // sort all other faces by angle with the dirInOF
6605         map< double, const SMDS_MeshElement* > angle2Face;
6606         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6607         for ( ; face != faces.end(); ++face )
6608         {
6609           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6610             continue;
6611           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6612           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6613           if ( angle < 0 ) angle += 2. * M_PI;
6614           angle2Face.insert( make_pair( angle, *face ));
6615         }
6616         if ( !angle2Face.empty() )
6617           outerFace2 = angle2Face.begin()->second;
6618       }
6619     }
6620     // store the found outer face and add its links to continue seaching from
6621     if ( outerFace2 )
6622     {
6623       _outerFaces.insert( outerFace );
6624       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6625       for ( int i = 0; i < nbNodes; ++i )
6626       {
6627         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6628         if ( visitedLinks.insert( link2 ).second )
6629           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6630       }
6631     }
6632     startLinks.pop_front();
6633   }
6634   _outerFacesFound = true;
6635
6636   if ( !seamLinks.empty() )
6637   {
6638     // There are internal boundaries touching the outher one,
6639     // find all faces of internal boundaries in order to find
6640     // faces of boundaries of holes, if any.
6641     
6642   }
6643   else
6644   {
6645     _outerFaces.clear();
6646   }
6647 }
6648
6649 //=======================================================================
6650 /*!
6651  * \brief Find elements of given type where the given point is IN or ON.
6652  *        Returns nb of found elements and elements them-selves.
6653  *
6654  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements 
6655  */
6656 //=======================================================================
6657
6658 int SMESH_ElementSearcherImpl::
6659 FindElementsByPoint(const gp_Pnt&                      point,
6660                     SMDSAbs_ElementType                type,
6661                     vector< const SMDS_MeshElement* >& foundElements)
6662 {
6663   foundElements.clear();
6664
6665   double tolerance = getTolerance();
6666
6667   // =================================================================================
6668   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6669   {
6670     if ( !_nodeSearcher )
6671       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6672
6673     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6674     if ( !closeNode ) return foundElements.size();
6675
6676     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6677       return foundElements.size(); // to far from any node
6678
6679     if ( type == SMDSAbs_Node )
6680     {
6681       foundElements.push_back( closeNode );
6682     }
6683     else
6684     {
6685       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6686       while ( elemIt->more() )
6687         foundElements.push_back( elemIt->next() );
6688     }
6689   }
6690   // =================================================================================
6691   else // elements more complex than 0D
6692   {
6693     if ( !_ebbTree || _elementType != type )
6694     {
6695       if ( _ebbTree ) delete _ebbTree;
6696       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6697     }
6698     TIDSortedElemSet suspectElems;
6699     _ebbTree->getElementsNearPoint( point, suspectElems );
6700     TIDSortedElemSet::iterator elem = suspectElems.begin();
6701     for ( ; elem != suspectElems.end(); ++elem )
6702       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6703         foundElements.push_back( *elem );
6704   }
6705   return foundElements.size();
6706 }
6707
6708 //=======================================================================
6709 /*!
6710  * \brief Find an element of given type most close to the given point
6711  *
6712  * WARNING: Only face search is implemeneted so far
6713  */
6714 //=======================================================================
6715
6716 const SMDS_MeshElement*
6717 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6718                                           SMDSAbs_ElementType type )
6719 {
6720   const SMDS_MeshElement* closestElem = 0;
6721
6722   if ( type == SMDSAbs_Face )
6723   {
6724     if ( !_ebbTree || _elementType != type )
6725     {
6726       if ( _ebbTree ) delete _ebbTree;
6727       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6728     }
6729     TIDSortedElemSet suspectElems;
6730     _ebbTree->getElementsNearPoint( point, suspectElems );
6731     
6732     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6733     {
6734       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox().CornerMin() +
6735                                  _ebbTree->getBox().CornerMax() );
6736       double radius;
6737       if ( _ebbTree->getBox().IsOut( point.XYZ() ))
6738         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6739       else
6740         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6741       while ( suspectElems.empty() )
6742       {
6743         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6744         radius *= 1.1;
6745       }
6746     }
6747     double minDist = std::numeric_limits<double>::max();
6748     multimap< double, const SMDS_MeshElement* > dist2face;
6749     TIDSortedElemSet::iterator elem = suspectElems.begin();
6750     for ( ; elem != suspectElems.end(); ++elem )
6751     {
6752       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6753                                                    point );
6754       if ( dist < minDist + 1e-10)
6755       {
6756         minDist = dist;
6757         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6758       }
6759     }
6760     if ( !dist2face.empty() )
6761     {
6762       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6763       closestElem = d2f->second;
6764       // if there are several elements at the same distance, select one
6765       // with GC closest to the point
6766       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6767       double minDistToGC = 0;
6768       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6769       {
6770         if ( minDistToGC == 0 )
6771         {
6772           gp_XYZ gc(0,0,0);
6773           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6774                            TXyzIterator(), gc ) / closestElem->NbNodes();
6775           minDistToGC = point.SquareDistance( gc );
6776         }
6777         gp_XYZ gc(0,0,0);
6778         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6779                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6780         double d = point.SquareDistance( gc );
6781         if ( d < minDistToGC )
6782         {
6783           minDistToGC = d;
6784           closestElem = d2f->second;
6785         }
6786       }
6787       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6788       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6789     }
6790   }
6791   else
6792   {
6793     // NOT IMPLEMENTED SO FAR
6794   }
6795   return closestElem;
6796 }
6797
6798
6799 //================================================================================
6800 /*!
6801  * \brief Classify the given point in the closed 2D mesh
6802  */
6803 //================================================================================
6804
6805 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6806 {
6807   double tolerance = getTolerance();
6808   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6809   {
6810     if ( _ebbTree ) delete _ebbTree;
6811     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6812   }
6813   // Algo: analyse transition of a line starting at the point through mesh boundary;
6814   // try three lines parallel to axis of the coordinate system and perform rough
6815   // analysis. If solution is not clear perform thorough analysis.
6816
6817   const int nbAxes = 3;
6818   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6819   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6820   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6821   multimap< int, int > nbInt2Axis; // to find the simplest case
6822   for ( int axis = 0; axis < nbAxes; ++axis )
6823   {
6824     gp_Ax1 lineAxis( point, axisDir[axis]);
6825     gp_Lin line    ( lineAxis );
6826
6827     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6828     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6829
6830     // Intersect faces with the line
6831
6832     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6833     TIDSortedElemSet::iterator face = suspectFaces.begin();
6834     for ( ; face != suspectFaces.end(); ++face )
6835     {
6836       // get face plane
6837       gp_XYZ fNorm;
6838       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6839       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6840
6841       // perform intersection
6842       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6843       if ( !intersection.IsDone() )
6844         continue;
6845       if ( intersection.IsInQuadric() )
6846       {
6847         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6848       }
6849       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6850       {
6851         gp_Pnt intersectionPoint = intersection.Point(1);
6852         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6853           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6854       }
6855     }
6856     // Analyse intersections roughly
6857
6858     int nbInter = u2inters.size();
6859     if ( nbInter == 0 )
6860       return TopAbs_OUT; 
6861
6862     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6863     if ( nbInter == 1 ) // not closed mesh
6864       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6865
6866     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6867       return TopAbs_ON;
6868
6869     if ( (f<0) == (l<0) )
6870       return TopAbs_OUT;
6871
6872     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6873     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
6874     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6875       return TopAbs_IN;
6876
6877     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6878
6879     if ( _outerFacesFound ) break; // pass to thorough analysis
6880
6881   } // three attempts - loop on CS axes
6882
6883   // Analyse intersections thoroughly.
6884   // We make two loops maximum, on the first one we only exclude touching intersections,
6885   // on the second, if situation is still unclear, we gather and use information on
6886   // position of faces (internal or outer). If faces position is already gathered,
6887   // we make the second loop right away.
6888
6889   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6890   {
6891     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6892     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6893     {
6894       int axis = nb_axis->second;
6895       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6896
6897       gp_Ax1 lineAxis( point, axisDir[axis]);
6898       gp_Lin line    ( lineAxis );
6899
6900       // add tangent intersections to u2inters
6901       double param;
6902       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6903       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6904         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6905           u2inters.insert(make_pair( param, *tgtInt ));
6906       tangentInters[ axis ].clear();
6907
6908       // Count intersections before and after the point excluding touching ones.
6909       // If hasPositionInfo we count intersections of outer boundary only
6910
6911       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6912       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6913       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6914       bool ok = ! u_int1->second._coincides;
6915       while ( ok && u_int1 != u2inters.end() )
6916       {
6917         double u = u_int1->first;
6918         bool touchingInt = false;
6919         if ( ++u_int2 != u2inters.end() )
6920         {
6921           // skip intersections at the same point (if the line passes through edge or node)
6922           int nbSamePnt = 0;
6923           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6924           {
6925             ++nbSamePnt;
6926             ++u_int2;
6927           }
6928
6929           // skip tangent intersections
6930           int nbTgt = 0;
6931           const SMDS_MeshElement* prevFace = u_int1->second._face;
6932           while ( ok && u_int2->second._coincides )
6933           {
6934             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6935               ok = false;
6936             else
6937             {
6938               nbTgt++;
6939               u_int2++;
6940               ok = ( u_int2 != u2inters.end() );
6941             }
6942           }
6943           if ( !ok ) break;
6944
6945           // skip intersections at the same point after tangent intersections
6946           if ( nbTgt > 0 )
6947           {
6948             double u2 = u_int2->first;
6949             ++u_int2;
6950             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6951             {
6952               ++nbSamePnt;
6953               ++u_int2;
6954             }
6955           }
6956           // decide if we skipped a touching intersection
6957           if ( nbSamePnt + nbTgt > 0 )
6958           {
6959             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6960             map< double, TInters >::iterator u_int = u_int1;
6961             for ( ; u_int != u_int2; ++u_int )
6962             {
6963               if ( u_int->second._coincides ) continue;
6964               double dot = u_int->second._faceNorm * line.Direction();
6965               if ( dot > maxDot ) maxDot = dot;
6966               if ( dot < minDot ) minDot = dot;
6967             }
6968             touchingInt = ( minDot*maxDot < 0 );
6969           }
6970         }
6971         if ( !touchingInt )
6972         {
6973           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6974           {
6975             if ( u < 0 )
6976               ++nbIntBeforePoint;
6977             else
6978               ++nbIntAfterPoint;
6979           }
6980           if ( u < f ) f = u;
6981           if ( u > l ) l = u;
6982         }
6983
6984         u_int1 = u_int2; // to next intersection
6985
6986       } // loop on intersections with one line
6987
6988       if ( ok )
6989       {
6990         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6991           return TopAbs_ON;
6992
6993         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
6994           return TopAbs_OUT; 
6995
6996         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6997           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6998
6999         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7000           return TopAbs_IN;
7001
7002         if ( (f<0) == (l<0) )
7003           return TopAbs_OUT;
7004
7005         if ( hasPositionInfo )
7006           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7007       }
7008     } // loop on intersections of the tree lines - thorough analysis
7009
7010     if ( !hasPositionInfo )
7011     {
7012       // gather info on faces position - is face in the outer boundary or not
7013       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7014       findOuterBoundary( u2inters.begin()->second._face );
7015     }
7016
7017   } // two attempts - with and w/o faces position info in the mesh
7018
7019   return TopAbs_UNKNOWN;
7020 }
7021
7022 //=======================================================================
7023 /*!
7024  * \brief Return elements possibly intersecting the line
7025  */
7026 //=======================================================================
7027
7028 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7029                                                      SMDSAbs_ElementType                type,
7030                                                      vector< const SMDS_MeshElement* >& foundElems)
7031 {
7032   if ( !_ebbTree || _elementType != type )
7033   {
7034     if ( _ebbTree ) delete _ebbTree;
7035     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7036   }
7037   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7038   _ebbTree->getElementsNearLine( line, suspectFaces );
7039   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7040 }
7041
7042 //=======================================================================
7043 /*!
7044  * \brief Return SMESH_ElementSearcher
7045  */
7046 //=======================================================================
7047
7048 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7049 {
7050   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7051 }
7052
7053 //=======================================================================
7054 /*!
7055  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7056  */
7057 //=======================================================================
7058
7059 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7060 {
7061   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7062 }
7063
7064 //=======================================================================
7065 /*!
7066  * \brief Return true if the point is IN or ON of the element
7067  */
7068 //=======================================================================
7069
7070 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7071 {
7072   if ( element->GetType() == SMDSAbs_Volume)
7073   {
7074     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7075   }
7076
7077   // get ordered nodes
7078
7079   vector< gp_XYZ > xyz;
7080   vector<const SMDS_MeshNode*> nodeList;
7081
7082   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7083   if ( element->IsQuadratic() ) {
7084     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7085       nodeIt = f->interlacedNodesElemIterator();
7086     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7087       nodeIt = e->interlacedNodesElemIterator();
7088   }
7089   while ( nodeIt->more() )
7090     {
7091       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7092       xyz.push_back( SMESH_TNodeXYZ(node) );
7093       nodeList.push_back(node);
7094     }
7095
7096   int i, nbNodes = element->NbNodes();
7097
7098   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7099   {
7100     // compute face normal
7101     gp_Vec faceNorm(0,0,0);
7102     xyz.push_back( xyz.front() );
7103     nodeList.push_back( nodeList.front() );
7104     for ( i = 0; i < nbNodes; ++i )
7105     {
7106       gp_Vec edge1( xyz[i+1], xyz[i]);
7107       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7108       faceNorm += edge1 ^ edge2;
7109     }
7110     double normSize = faceNorm.Magnitude();
7111     if ( normSize <= tol )
7112     {
7113       // degenerated face: point is out if it is out of all face edges
7114       for ( i = 0; i < nbNodes; ++i )
7115       {
7116         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7117         if ( !IsOut( &edge, point, tol ))
7118           return false;
7119       }
7120       return true;
7121     }
7122     faceNorm /= normSize;
7123
7124     // check if the point lays on face plane
7125     gp_Vec n2p( xyz[0], point );
7126     if ( fabs( n2p * faceNorm ) > tol )
7127       return true; // not on face plane
7128
7129     // check if point is out of face boundary:
7130     // define it by closest transition of a ray point->infinity through face boundary
7131     // on the face plane.
7132     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7133     // to find intersections of the ray with the boundary.
7134     gp_Vec ray = n2p;
7135     gp_Vec plnNorm = ray ^ faceNorm;
7136     normSize = plnNorm.Magnitude();
7137     if ( normSize <= tol ) return false; // point coincides with the first node
7138     plnNorm /= normSize;
7139     // for each node of the face, compute its signed distance to the plane
7140     vector<double> dist( nbNodes + 1);
7141     for ( i = 0; i < nbNodes; ++i )
7142     {
7143       gp_Vec n2p( xyz[i], point );
7144       dist[i] = n2p * plnNorm;
7145     }
7146     dist.back() = dist.front();
7147     // find the closest intersection
7148     int    iClosest = -1;
7149     double rClosest, distClosest = 1e100;;
7150     gp_Pnt pClosest;
7151     for ( i = 0; i < nbNodes; ++i )
7152     {
7153       double r;
7154       if ( fabs( dist[i]) < tol )
7155         r = 0.;
7156       else if ( fabs( dist[i+1]) < tol )
7157         r = 1.;
7158       else if ( dist[i] * dist[i+1] < 0 )
7159         r = dist[i] / ( dist[i] - dist[i+1] );
7160       else
7161         continue; // no intersection
7162       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7163       gp_Vec p2int ( point, pInt);
7164       if ( p2int * ray > -tol ) // right half-space
7165       {
7166         double intDist = p2int.SquareMagnitude();
7167         if ( intDist < distClosest )
7168         {
7169           iClosest = i;
7170           rClosest = r;
7171           pClosest = pInt;
7172           distClosest = intDist;
7173         }
7174       }
7175     }
7176     if ( iClosest < 0 )
7177       return true; // no intesections - out
7178
7179     // analyse transition
7180     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7181     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7182     gp_Vec p2int ( point, pClosest );
7183     bool out = (edgeNorm * p2int) < -tol;
7184     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7185       return out;
7186
7187     // ray pass through a face node; analyze transition through an adjacent edge
7188     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7189     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7190     gp_Vec edgeAdjacent( p1, p2 );
7191     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7192     bool out2 = (edgeNorm2 * p2int) < -tol;
7193
7194     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7195     return covexCorner ? (out || out2) : (out && out2);
7196   }
7197   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7198   {
7199     // point is out of edge if it is NOT ON any straight part of edge
7200     // (we consider quadratic edge as being composed of two straight parts)
7201     for ( i = 1; i < nbNodes; ++i )
7202     {
7203       gp_Vec edge( xyz[i-1], xyz[i]);
7204       gp_Vec n1p ( xyz[i-1], point);
7205       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7206       if ( dist > tol )
7207         continue;
7208       gp_Vec n2p( xyz[i], point );
7209       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7210         continue;
7211       return false; // point is ON this part
7212     }
7213     return true;
7214   }
7215   // Node or 0D element -------------------------------------------------------------------------
7216   {
7217     gp_Vec n2p ( xyz[0], point );
7218     return n2p.Magnitude() <= tol;
7219   }
7220   return true;
7221 }
7222
7223 //=======================================================================
7224
7225 namespace
7226 {
7227   // Position of a point relative to a segment
7228   //            .           .
7229   //            .  LEFT     .
7230   //            .           .
7231   //  VERTEX 1  o----ON----->  VERTEX 2
7232   //            .           .
7233   //            .  RIGHT    .
7234   //            .           .
7235   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7236                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7237   struct PointPos
7238   {
7239     PositionName _name; 
7240     int          _index; // index of vertex or segment
7241
7242     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7243     bool operator < (const PointPos& other ) const
7244     {
7245       if ( _name == other._name )
7246         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7247       return _name < other._name;
7248     }
7249   };
7250
7251   //================================================================================
7252   /*!
7253    * \brief Return of a point relative to a segment
7254    *  \param point2D      - the point to analyze position of
7255    *  \param xyVec        - end points of segments
7256    *  \param index0       - 0-based index of the first point of segment
7257    *  \param posToFindOut - flags of positions to detect
7258    *  \retval PointPos - point position
7259    */
7260   //================================================================================
7261
7262   PointPos getPointPosition( const gp_XY& point2D,
7263                              const gp_XY* segEnds,
7264                              const int    index0 = 0,
7265                              const int    posToFindOut = POS_ALL)
7266   {
7267     const gp_XY& p1 = segEnds[ index0   ];
7268     const gp_XY& p2 = segEnds[ index0+1 ];
7269     const gp_XY grad = p2 - p1;
7270
7271     if ( posToFindOut & POS_VERTEX )
7272     {
7273       // check if the point2D is at "vertex 1" zone
7274       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7275                                   p1.Y() + grad.X() ) };
7276       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7277         return PointPos( POS_VERTEX, index0 );
7278
7279       // check if the point2D is at "vertex 2" zone
7280       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7281                                   p2.Y() + grad.X() ) };
7282       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7283         return PointPos( POS_VERTEX, index0 + 1);
7284     }
7285     double edgeEquation =
7286       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7287     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7288   }
7289 }
7290
7291 //=======================================================================
7292 /*!
7293  * \brief Return minimal distance from a point to a face
7294  *
7295  * Currently we ignore non-planarity and 2nd order of face
7296  */
7297 //=======================================================================
7298
7299 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7300                                       const gp_Pnt&        point )
7301 {
7302   double badDistance = -1;
7303   if ( !face ) return badDistance;
7304
7305   // coordinates of nodes (medium nodes, if any, ignored)
7306   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7307   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7308   xyz.resize( face->NbCornerNodes()+1 );
7309
7310   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7311   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7312   gp_Trsf trsf;
7313   gp_Vec OZ ( xyz[0], xyz[1] );
7314   gp_Vec OX ( xyz[0], xyz[2] );
7315   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7316   {
7317     if ( xyz.size() < 4 ) return badDistance;
7318     OZ = gp_Vec ( xyz[0], xyz[2] );
7319     OX = gp_Vec ( xyz[0], xyz[3] );
7320   }
7321   gp_Ax3 tgtCS;
7322   try {
7323     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7324   }
7325   catch ( Standard_Failure ) {
7326     return badDistance;
7327   }
7328   trsf.SetTransformation( tgtCS );
7329
7330   // move all the nodes to 2D
7331   vector<gp_XY> xy( xyz.size() );
7332   for ( size_t i = 0;i < xyz.size()-1; ++i )
7333   {
7334     gp_XYZ p3d = xyz[i];
7335     trsf.Transforms( p3d );
7336     xy[i].SetCoord( p3d.X(), p3d.Z() );
7337   }
7338   xyz.back() = xyz.front();
7339   xy.back() = xy.front();
7340
7341   // // move the point in 2D
7342   gp_XYZ tmpPnt = point.XYZ();
7343   trsf.Transforms( tmpPnt );
7344   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7345
7346   // loop on segments of the face to analyze point position ralative to the face
7347   set< PointPos > pntPosSet;
7348   for ( size_t i = 1; i < xy.size(); ++i )
7349   {
7350     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7351     pntPosSet.insert( pos );
7352   }
7353
7354   // compute distance
7355   PointPos pos = *pntPosSet.begin();
7356   // cout << "Face " << face->GetID() << " DIST: ";
7357   switch ( pos._name )
7358   {
7359   case POS_LEFT: {
7360     // point is most close to a segment
7361     gp_Vec p0p1( point, xyz[ pos._index ] );
7362     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7363     p1p2.Normalize();
7364     double projDist = p0p1 * p1p2; // distance projected to the segment
7365     gp_Vec projVec = p1p2 * projDist;
7366     gp_Vec distVec = p0p1 - projVec;
7367     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7368     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7369     return distVec.Magnitude();
7370   }
7371   case POS_RIGHT: {
7372     // point is inside the face
7373     double distToFacePlane = tmpPnt.Y();
7374     // cout << distToFacePlane << ", INSIDE " << endl;
7375     return Abs( distToFacePlane );
7376   }
7377   case POS_VERTEX: {
7378     // point is most close to a node
7379     gp_Vec distVec( point, xyz[ pos._index ]);
7380     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7381     return distVec.Magnitude();
7382   }
7383   }
7384   return badDistance;
7385 }
7386
7387 //=======================================================================
7388 //function : SimplifyFace
7389 //purpose  :
7390 //=======================================================================
7391 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7392                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7393                                     vector<int>&                        quantities) const
7394 {
7395   int nbNodes = faceNodes.size();
7396
7397   if (nbNodes < 3)
7398     return 0;
7399
7400   set<const SMDS_MeshNode*> nodeSet;
7401
7402   // get simple seq of nodes
7403   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7404   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7405   int iSimple = 0, nbUnique = 0;
7406
7407   simpleNodes[iSimple++] = faceNodes[0];
7408   nbUnique++;
7409   for (int iCur = 1; iCur < nbNodes; iCur++) {
7410     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7411       simpleNodes[iSimple++] = faceNodes[iCur];
7412       if (nodeSet.insert( faceNodes[iCur] ).second)
7413         nbUnique++;
7414     }
7415   }
7416   int nbSimple = iSimple;
7417   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7418     nbSimple--;
7419     iSimple--;
7420   }
7421
7422   if (nbUnique < 3)
7423     return 0;
7424
7425   // separate loops
7426   int nbNew = 0;
7427   bool foundLoop = (nbSimple > nbUnique);
7428   while (foundLoop) {
7429     foundLoop = false;
7430     set<const SMDS_MeshNode*> loopSet;
7431     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7432       const SMDS_MeshNode* n = simpleNodes[iSimple];
7433       if (!loopSet.insert( n ).second) {
7434         foundLoop = true;
7435
7436         // separate loop
7437         int iC = 0, curLast = iSimple;
7438         for (; iC < curLast; iC++) {
7439           if (simpleNodes[iC] == n) break;
7440         }
7441         int loopLen = curLast - iC;
7442         if (loopLen > 2) {
7443           // create sub-element
7444           nbNew++;
7445           quantities.push_back(loopLen);
7446           for (; iC < curLast; iC++) {
7447             poly_nodes.push_back(simpleNodes[iC]);
7448           }
7449         }
7450         // shift the rest nodes (place from the first loop position)
7451         for (iC = curLast + 1; iC < nbSimple; iC++) {
7452           simpleNodes[iC - loopLen] = simpleNodes[iC];
7453         }
7454         nbSimple -= loopLen;
7455         iSimple -= loopLen;
7456       }
7457     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7458   } // while (foundLoop)
7459
7460   if (iSimple > 2) {
7461     nbNew++;
7462     quantities.push_back(iSimple);
7463     for (int i = 0; i < iSimple; i++)
7464       poly_nodes.push_back(simpleNodes[i]);
7465   }
7466
7467   return nbNew;
7468 }
7469
7470 //=======================================================================
7471 //function : MergeNodes
7472 //purpose  : In each group, the cdr of nodes are substituted by the first one
7473 //           in all elements.
7474 //=======================================================================
7475
7476 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7477 {
7478   MESSAGE("MergeNodes");
7479   myLastCreatedElems.Clear();
7480   myLastCreatedNodes.Clear();
7481
7482   SMESHDS_Mesh* aMesh = GetMeshDS();
7483
7484   TNodeNodeMap nodeNodeMap; // node to replace - new node
7485   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7486   list< int > rmElemIds, rmNodeIds;
7487
7488   // Fill nodeNodeMap and elems
7489
7490   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7491   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7492     list<const SMDS_MeshNode*>& nodes = *grIt;
7493     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7494     const SMDS_MeshNode* nToKeep = *nIt;
7495     //MESSAGE("node to keep " << nToKeep->GetID());
7496     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7497       const SMDS_MeshNode* nToRemove = *nIt;
7498       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7499       if ( nToRemove != nToKeep ) {
7500         //MESSAGE("  node to remove " << nToRemove->GetID());
7501         rmNodeIds.push_back( nToRemove->GetID() );
7502         AddToSameGroups( nToKeep, nToRemove, aMesh );
7503       }
7504
7505       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7506       while ( invElemIt->more() ) {
7507         const SMDS_MeshElement* elem = invElemIt->next();
7508         elems.insert(elem);
7509       }
7510     }
7511   }
7512   // Change element nodes or remove an element
7513
7514   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7515   for ( ; eIt != elems.end(); eIt++ ) {
7516     const SMDS_MeshElement* elem = *eIt;
7517     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7518     int nbNodes = elem->NbNodes();
7519     int aShapeId = FindShape( elem );
7520
7521     set<const SMDS_MeshNode*> nodeSet;
7522     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7523     int iUnique = 0, iCur = 0, nbRepl = 0;
7524     vector<int> iRepl( nbNodes );
7525
7526     // get new seq of nodes
7527     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7528     while ( itN->more() ) {
7529       const SMDS_MeshNode* n =
7530         static_cast<const SMDS_MeshNode*>( itN->next() );
7531
7532       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7533       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7534         n = (*nnIt).second;
7535         // BUG 0020185: begin
7536         {
7537           bool stopRecur = false;
7538           set<const SMDS_MeshNode*> nodesRecur;
7539           nodesRecur.insert(n);
7540           while (!stopRecur) {
7541             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7542             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7543               n = (*nnIt_i).second;
7544               if (!nodesRecur.insert(n).second) {
7545                 // error: recursive dependancy
7546                 stopRecur = true;
7547               }
7548             }
7549             else
7550               stopRecur = true;
7551           }
7552         }
7553         // BUG 0020185: end
7554       }
7555       curNodes[ iCur ] = n;
7556       bool isUnique = nodeSet.insert( n ).second;
7557       if ( isUnique )
7558         uniqueNodes[ iUnique++ ] = n;
7559       else
7560         iRepl[ nbRepl++ ] = iCur;
7561       iCur++;
7562     }
7563
7564     // Analyse element topology after replacement
7565
7566     bool isOk = true;
7567     int nbUniqueNodes = nodeSet.size();
7568     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7569     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7570       // Polygons and Polyhedral volumes
7571       if (elem->IsPoly()) {
7572
7573         if (elem->GetType() == SMDSAbs_Face) {
7574           // Polygon
7575           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7576           int inode = 0;
7577           for (; inode < nbNodes; inode++) {
7578             face_nodes[inode] = curNodes[inode];
7579           }
7580
7581           vector<const SMDS_MeshNode *> polygons_nodes;
7582           vector<int> quantities;
7583           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7584           if (nbNew > 0) {
7585             inode = 0;
7586             for (int iface = 0; iface < nbNew; iface++) {
7587               int nbNodes = quantities[iface];
7588               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7589               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7590                 poly_nodes[ii] = polygons_nodes[inode];
7591               }
7592               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7593               myLastCreatedElems.Append(newElem);
7594               if (aShapeId)
7595                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7596             }
7597
7598             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7599             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7600             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7601             int quid =0;
7602             if (nbNew > 0) quid = nbNew - 1;
7603             vector<int> newquant(quantities.begin()+quid, quantities.end());
7604             const SMDS_MeshElement* newElem = 0;
7605             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7606             myLastCreatedElems.Append(newElem);
7607             if ( aShapeId && newElem )
7608               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7609             rmElemIds.push_back(elem->GetID());
7610           }
7611           else {
7612             rmElemIds.push_back(elem->GetID());
7613           }
7614
7615         }
7616         else if (elem->GetType() == SMDSAbs_Volume) {
7617           // Polyhedral volume
7618           if (nbUniqueNodes < 4) {
7619             rmElemIds.push_back(elem->GetID());
7620           }
7621           else {
7622             // each face has to be analyzed in order to check volume validity
7623             const SMDS_VtkVolume* aPolyedre =
7624               dynamic_cast<const SMDS_VtkVolume*>( elem );
7625             if (aPolyedre) {
7626               int nbFaces = aPolyedre->NbFaces();
7627
7628               vector<const SMDS_MeshNode *> poly_nodes;
7629               vector<int> quantities;
7630
7631               for (int iface = 1; iface <= nbFaces; iface++) {
7632                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7633                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7634
7635                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7636                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7637                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7638                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7639                     faceNode = (*nnIt).second;
7640                   }
7641                   faceNodes[inode - 1] = faceNode;
7642                 }
7643
7644                 SimplifyFace(faceNodes, poly_nodes, quantities);
7645               }
7646
7647               if (quantities.size() > 3) {
7648                 // to be done: remove coincident faces
7649               }
7650
7651               if (quantities.size() > 3)
7652                 {
7653                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7654                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7655                   const SMDS_MeshElement* newElem = 0;
7656                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7657                   myLastCreatedElems.Append(newElem);
7658                   if ( aShapeId && newElem )
7659                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7660                   rmElemIds.push_back(elem->GetID());
7661                 }
7662             }
7663             else {
7664               rmElemIds.push_back(elem->GetID());
7665             }
7666           }
7667         }
7668         else {
7669         }
7670
7671         continue;
7672       } // poly element
7673
7674       // Regular elements
7675       // TODO not all the possible cases are solved. Find something more generic?
7676       switch ( nbNodes ) {
7677       case 2: ///////////////////////////////////// EDGE
7678         isOk = false; break;
7679       case 3: ///////////////////////////////////// TRIANGLE
7680         isOk = false; break;
7681       case 4:
7682         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7683           isOk = false;
7684         else { //////////////////////////////////// QUADRANGLE
7685           if ( nbUniqueNodes < 3 )
7686             isOk = false;
7687           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7688             isOk = false; // opposite nodes stick
7689           //MESSAGE("isOk " << isOk);
7690         }
7691         break;
7692       case 6: ///////////////////////////////////// PENTAHEDRON
7693         if ( nbUniqueNodes == 4 ) {
7694           // ---------------------------------> tetrahedron
7695           if (nbRepl == 3 &&
7696               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7697             // all top nodes stick: reverse a bottom
7698             uniqueNodes[ 0 ] = curNodes [ 1 ];
7699             uniqueNodes[ 1 ] = curNodes [ 0 ];
7700           }
7701           else if (nbRepl == 3 &&
7702                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7703             // all bottom nodes stick: set a top before
7704             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7705             uniqueNodes[ 0 ] = curNodes [ 3 ];
7706             uniqueNodes[ 1 ] = curNodes [ 4 ];
7707             uniqueNodes[ 2 ] = curNodes [ 5 ];
7708           }
7709           else if (nbRepl == 4 &&
7710                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7711             // a lateral face turns into a line: reverse a bottom
7712             uniqueNodes[ 0 ] = curNodes [ 1 ];
7713             uniqueNodes[ 1 ] = curNodes [ 0 ];
7714           }
7715           else
7716             isOk = false;
7717         }
7718         else if ( nbUniqueNodes == 5 ) {
7719           // PENTAHEDRON --------------------> 2 tetrahedrons
7720           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7721             // a bottom node sticks with a linked top one
7722             // 1.
7723             SMDS_MeshElement* newElem =
7724               aMesh->AddVolume(curNodes[ 3 ],
7725                                curNodes[ 4 ],
7726                                curNodes[ 5 ],
7727                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7728             myLastCreatedElems.Append(newElem);
7729             if ( aShapeId )
7730               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7731             // 2. : reverse a bottom
7732             uniqueNodes[ 0 ] = curNodes [ 1 ];
7733             uniqueNodes[ 1 ] = curNodes [ 0 ];
7734             nbUniqueNodes = 4;
7735           }
7736           else
7737             isOk = false;
7738         }
7739         else
7740           isOk = false;
7741         break;
7742       case 8: {
7743         if(elem->IsQuadratic()) { // Quadratic quadrangle
7744           //   1    5    2
7745           //    +---+---+
7746           //    |       |
7747           //    |       |
7748           //   4+       +6
7749           //    |       |
7750           //    |       |
7751           //    +---+---+
7752           //   0    7    3
7753           isOk = false;
7754           if(nbRepl==2) {
7755             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7756           }
7757           if(nbRepl==3) {
7758             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7759             nbUniqueNodes = 6;
7760             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7761               uniqueNodes[0] = curNodes[0];
7762               uniqueNodes[1] = curNodes[2];
7763               uniqueNodes[2] = curNodes[3];
7764               uniqueNodes[3] = curNodes[5];
7765               uniqueNodes[4] = curNodes[6];
7766               uniqueNodes[5] = curNodes[7];
7767               isOk = true;
7768             }
7769             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7770               uniqueNodes[0] = curNodes[0];
7771               uniqueNodes[1] = curNodes[1];
7772               uniqueNodes[2] = curNodes[2];
7773               uniqueNodes[3] = curNodes[4];
7774               uniqueNodes[4] = curNodes[5];
7775               uniqueNodes[5] = curNodes[6];
7776               isOk = true;
7777             }
7778             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7779               uniqueNodes[0] = curNodes[1];
7780               uniqueNodes[1] = curNodes[2];
7781               uniqueNodes[2] = curNodes[3];
7782               uniqueNodes[3] = curNodes[5];
7783               uniqueNodes[4] = curNodes[6];
7784               uniqueNodes[5] = curNodes[0];
7785               isOk = true;
7786             }
7787             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7788               uniqueNodes[0] = curNodes[0];
7789               uniqueNodes[1] = curNodes[1];
7790               uniqueNodes[2] = curNodes[3];
7791               uniqueNodes[3] = curNodes[4];
7792               uniqueNodes[4] = curNodes[6];
7793               uniqueNodes[5] = curNodes[7];
7794               isOk = true;
7795             }
7796             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7797               uniqueNodes[0] = curNodes[0];
7798               uniqueNodes[1] = curNodes[2];
7799               uniqueNodes[2] = curNodes[3];
7800               uniqueNodes[3] = curNodes[1];
7801               uniqueNodes[4] = curNodes[6];
7802               uniqueNodes[5] = curNodes[7];
7803               isOk = true;
7804             }
7805             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7806               uniqueNodes[0] = curNodes[0];
7807               uniqueNodes[1] = curNodes[1];
7808               uniqueNodes[2] = curNodes[2];
7809               uniqueNodes[3] = curNodes[4];
7810               uniqueNodes[4] = curNodes[5];
7811               uniqueNodes[5] = curNodes[7];
7812               isOk = true;
7813             }
7814             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7815               uniqueNodes[0] = curNodes[0];
7816               uniqueNodes[1] = curNodes[1];
7817               uniqueNodes[2] = curNodes[3];
7818               uniqueNodes[3] = curNodes[4];
7819               uniqueNodes[4] = curNodes[2];
7820               uniqueNodes[5] = curNodes[7];
7821               isOk = true;
7822             }
7823             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7824               uniqueNodes[0] = curNodes[0];
7825               uniqueNodes[1] = curNodes[1];
7826               uniqueNodes[2] = curNodes[2];
7827               uniqueNodes[3] = curNodes[4];
7828               uniqueNodes[4] = curNodes[5];
7829               uniqueNodes[5] = curNodes[3];
7830               isOk = true;
7831             }
7832           }
7833           if(nbRepl==4) {
7834             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7835           }
7836           if(nbRepl==5) {
7837             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7838           }
7839           break;
7840         }
7841         //////////////////////////////////// HEXAHEDRON
7842         isOk = false;
7843         SMDS_VolumeTool hexa (elem);
7844         hexa.SetExternalNormal();
7845         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7846           //////////////////////// HEX ---> 1 tetrahedron
7847           for ( int iFace = 0; iFace < 6; iFace++ ) {
7848             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7849             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7850                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7851                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7852               // one face turns into a point ...
7853               int iOppFace = hexa.GetOppFaceIndex( iFace );
7854               ind = hexa.GetFaceNodesIndices( iOppFace );
7855               int nbStick = 0;
7856               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7857                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7858                   nbStick++;
7859               }
7860               if ( nbStick == 1 ) {
7861                 // ... and the opposite one - into a triangle.
7862                 // set a top node
7863                 ind = hexa.GetFaceNodesIndices( iFace );
7864                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7865                 isOk = true;
7866               }
7867               break;
7868             }
7869           }
7870         }
7871         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7872           //////////////////////// HEX ---> 1 prism
7873           int nbTria = 0, iTria[3];
7874           const int *ind; // indices of face nodes
7875           // look for triangular faces
7876           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7877             ind = hexa.GetFaceNodesIndices( iFace );
7878             TIDSortedNodeSet faceNodes;
7879             for ( iCur = 0; iCur < 4; iCur++ )
7880               faceNodes.insert( curNodes[ind[iCur]] );
7881             if ( faceNodes.size() == 3 )
7882               iTria[ nbTria++ ] = iFace;
7883           }
7884           // check if triangles are opposite
7885           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7886           {
7887             isOk = true;
7888             // set nodes of the bottom triangle
7889             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7890             vector<int> indB;
7891             for ( iCur = 0; iCur < 4; iCur++ )
7892               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7893                 indB.push_back( ind[iCur] );
7894             if ( !hexa.IsForward() )
7895               std::swap( indB[0], indB[2] );
7896             for ( iCur = 0; iCur < 3; iCur++ )
7897               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7898             // set nodes of the top triangle
7899             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7900             for ( iCur = 0; iCur < 3; ++iCur )
7901               for ( int j = 0; j < 4; ++j )
7902                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7903                 {
7904                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7905                   break;
7906                 }
7907           }
7908           break;
7909         }
7910         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7911           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7912           for ( int iFace = 0; iFace < 6; iFace++ ) {
7913             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7914             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7915                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7916                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7917               // one face turns into a point ...
7918               int iOppFace = hexa.GetOppFaceIndex( iFace );
7919               ind = hexa.GetFaceNodesIndices( iOppFace );
7920               int nbStick = 0;
7921               iUnique = 2;  // reverse a tetrahedron 1 bottom
7922               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7923                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7924                   nbStick++;
7925                 else if ( iUnique >= 0 )
7926                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7927               }
7928               if ( nbStick == 0 ) {
7929                 // ... and the opposite one is a quadrangle
7930                 // set a top node
7931                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7932                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7933                 nbUniqueNodes = 4;
7934                 // tetrahedron 2
7935                 SMDS_MeshElement* newElem =
7936                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7937                                    curNodes[ind[ 3 ]],
7938                                    curNodes[ind[ 2 ]],
7939                                    curNodes[indTop[ 0 ]]);
7940                 myLastCreatedElems.Append(newElem);
7941                 if ( aShapeId )
7942                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7943                 isOk = true;
7944               }
7945               break;
7946             }
7947           }
7948         }
7949         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7950           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7951           // find indices of quad and tri faces
7952           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7953           for ( iFace = 0; iFace < 6; iFace++ ) {
7954             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7955             nodeSet.clear();
7956             for ( iCur = 0; iCur < 4; iCur++ )
7957               nodeSet.insert( curNodes[ind[ iCur ]] );
7958             nbUniqueNodes = nodeSet.size();
7959             if ( nbUniqueNodes == 3 )
7960               iTriFace[ nbTri++ ] = iFace;
7961             else if ( nbUniqueNodes == 4 )
7962               iQuadFace[ nbQuad++ ] = iFace;
7963           }
7964           if (nbQuad == 2 && nbTri == 4 &&
7965               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7966             // 2 opposite quadrangles stuck with a diagonal;
7967             // sample groups of merged indices: (0-4)(2-6)
7968             // --------------------------------------------> 2 tetrahedrons
7969             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7970             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7971             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7972             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7973                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7974               // stuck with 0-2 diagonal
7975               i0  = ind1[ 3 ];
7976               i1d = ind1[ 0 ];
7977               i2  = ind1[ 1 ];
7978               i3d = ind1[ 2 ];
7979               i0t = ind2[ 1 ];
7980               i2t = ind2[ 3 ];
7981             }
7982             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7983                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7984               // stuck with 1-3 diagonal
7985               i0  = ind1[ 0 ];
7986               i1d = ind1[ 1 ];
7987               i2  = ind1[ 2 ];
7988               i3d = ind1[ 3 ];
7989               i0t = ind2[ 0 ];
7990               i2t = ind2[ 1 ];
7991             }
7992             else {
7993               ASSERT(0);
7994             }
7995             // tetrahedron 1
7996             uniqueNodes[ 0 ] = curNodes [ i0 ];
7997             uniqueNodes[ 1 ] = curNodes [ i1d ];
7998             uniqueNodes[ 2 ] = curNodes [ i3d ];
7999             uniqueNodes[ 3 ] = curNodes [ i0t ];
8000             nbUniqueNodes = 4;
8001             // tetrahedron 2
8002             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8003                                                          curNodes[ i2 ],
8004                                                          curNodes[ i3d ],
8005                                                          curNodes[ i2t ]);
8006             myLastCreatedElems.Append(newElem);
8007             if ( aShapeId )
8008               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8009             isOk = true;
8010           }
8011           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8012                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8013             // --------------------------------------------> prism
8014             // find 2 opposite triangles
8015             nbUniqueNodes = 6;
8016             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8017               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8018                 // find indices of kept and replaced nodes
8019                 // and fill unique nodes of 2 opposite triangles
8020                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8021                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8022                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8023                 // fill unique nodes
8024                 iUnique = 0;
8025                 isOk = true;
8026                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8027                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8028                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8029                   if ( n == nInit ) {
8030                     // iCur of a linked node of the opposite face (make normals co-directed):
8031                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8032                     // check that correspondent corners of triangles are linked
8033                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8034                       isOk = false;
8035                     else {
8036                       uniqueNodes[ iUnique ] = n;
8037                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8038                       iUnique++;
8039                     }
8040                   }
8041                 }
8042                 break;
8043               }
8044             }
8045           }
8046         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8047         else
8048         {
8049           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8050         }
8051         break;
8052       } // HEXAHEDRON
8053
8054       default:
8055         isOk = false;
8056       } // switch ( nbNodes )
8057
8058     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8059
8060     if ( isOk ) { // the elem remains valid after sticking nodes
8061       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8062       {
8063         // Change nodes of polyedre
8064         const SMDS_VtkVolume* aPolyedre =
8065           dynamic_cast<const SMDS_VtkVolume*>( elem );
8066         if (aPolyedre) {
8067           int nbFaces = aPolyedre->NbFaces();
8068
8069           vector<const SMDS_MeshNode *> poly_nodes;
8070           vector<int> quantities (nbFaces);
8071
8072           for (int iface = 1; iface <= nbFaces; iface++) {
8073             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8074             quantities[iface - 1] = nbFaceNodes;
8075
8076             for (inode = 1; inode <= nbFaceNodes; inode++) {
8077               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8078
8079               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8080               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8081                 curNode = (*nnIt).second;
8082               }
8083               poly_nodes.push_back(curNode);
8084             }
8085           }
8086           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8087         }
8088       }
8089       else // replace non-polyhedron elements
8090       {
8091         const SMDSAbs_ElementType etyp = elem->GetType();
8092         const int elemId               = elem->GetID();
8093         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8094         uniqueNodes.resize(nbUniqueNodes);
8095
8096         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8097
8098         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8099         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8100         if ( sm && newElem )
8101           sm->AddElement( newElem );
8102         if ( elem != newElem )
8103           ReplaceElemInGroups( elem, newElem, aMesh );
8104       }
8105     }
8106     else {
8107       // Remove invalid regular element or invalid polygon
8108       rmElemIds.push_back( elem->GetID() );
8109     }
8110
8111   } // loop on elements
8112
8113   // Remove bad elements, then equal nodes (order important)
8114
8115   Remove( rmElemIds, false );
8116   Remove( rmNodeIds, true );
8117
8118 }
8119
8120
8121 // ========================================================
8122 // class   : SortableElement
8123 // purpose : allow sorting elements basing on their nodes
8124 // ========================================================
8125 class SortableElement : public set <const SMDS_MeshElement*>
8126 {
8127 public:
8128
8129   SortableElement( const SMDS_MeshElement* theElem )
8130   {
8131     myElem = theElem;
8132     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8133     while ( nodeIt->more() )
8134       this->insert( nodeIt->next() );
8135   }
8136
8137   const SMDS_MeshElement* Get() const
8138   { return myElem; }
8139
8140   void Set(const SMDS_MeshElement* e) const
8141   { myElem = e; }
8142
8143
8144 private:
8145   mutable const SMDS_MeshElement* myElem;
8146 };
8147
8148 //=======================================================================
8149 //function : FindEqualElements
8150 //purpose  : Return list of group of elements built on the same nodes.
8151 //           Search among theElements or in the whole mesh if theElements is empty
8152 //=======================================================================
8153 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
8154                                          TListOfListOfElementsID &      theGroupsOfElementsID)
8155 {
8156   myLastCreatedElems.Clear();
8157   myLastCreatedNodes.Clear();
8158
8159   typedef set<const SMDS_MeshElement*> TElemsSet;
8160   typedef map< SortableElement, int > TMapOfNodeSet;
8161   typedef list<int> TGroupOfElems;
8162
8163   TElemsSet elems;
8164   if ( theElements.empty() )
8165   { // get all elements in the mesh
8166     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8167     while ( eIt->more() )
8168       elems.insert( elems.end(), eIt->next());
8169   }
8170   else
8171     elems = theElements;
8172
8173   vector< TGroupOfElems > arrayOfGroups;
8174   TGroupOfElems groupOfElems;
8175   TMapOfNodeSet mapOfNodeSet;
8176
8177   TElemsSet::iterator elemIt = elems.begin();
8178   for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
8179     const SMDS_MeshElement* curElem = *elemIt;
8180     SortableElement SE(curElem);
8181     int ind = -1;
8182     // check uniqueness
8183     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8184     if( !(pp.second) ) {
8185       TMapOfNodeSet::iterator& itSE = pp.first;
8186       ind = (*itSE).second;
8187       arrayOfGroups[ind].push_back(curElem->GetID());
8188     }
8189     else {
8190       groupOfElems.clear();
8191       groupOfElems.push_back(curElem->GetID());
8192       arrayOfGroups.push_back(groupOfElems);
8193       i++;
8194     }
8195   }
8196
8197   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8198   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8199     groupOfElems = *groupIt;
8200     if ( groupOfElems.size() > 1 ) {
8201       groupOfElems.sort();
8202       theGroupsOfElementsID.push_back(groupOfElems);
8203     }
8204   }
8205 }
8206
8207 //=======================================================================
8208 //function : MergeElements
8209 //purpose  : In each given group, substitute all elements by the first one.
8210 //=======================================================================
8211
8212 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8213 {
8214   myLastCreatedElems.Clear();
8215   myLastCreatedNodes.Clear();
8216
8217   typedef list<int> TListOfIDs;
8218   TListOfIDs rmElemIds; // IDs of elems to remove
8219
8220   SMESHDS_Mesh* aMesh = GetMeshDS();
8221
8222   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8223   while ( groupsIt != theGroupsOfElementsID.end() ) {
8224     TListOfIDs& aGroupOfElemID = *groupsIt;
8225     aGroupOfElemID.sort();
8226     int elemIDToKeep = aGroupOfElemID.front();
8227     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8228     aGroupOfElemID.pop_front();
8229     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8230     while ( idIt != aGroupOfElemID.end() ) {
8231       int elemIDToRemove = *idIt;
8232       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8233       // add the kept element in groups of removed one (PAL15188)
8234       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8235       rmElemIds.push_back( elemIDToRemove );
8236       ++idIt;
8237     }
8238     ++groupsIt;
8239   }
8240
8241   Remove( rmElemIds, false );
8242 }
8243
8244 //=======================================================================
8245 //function : MergeEqualElements
8246 //purpose  : Remove all but one of elements built on the same nodes.
8247 //=======================================================================
8248
8249 void SMESH_MeshEditor::MergeEqualElements()
8250 {
8251   set<const SMDS_MeshElement*> aMeshElements; /* empty input -
8252                                                  to merge equal elements in the whole mesh */
8253   TListOfListOfElementsID aGroupsOfElementsID;
8254   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8255   MergeElements(aGroupsOfElementsID);
8256 }
8257
8258 //=======================================================================
8259 //function : FindFaceInSet
8260 //purpose  : Return a face having linked nodes n1 and n2 and which is
8261 //           - not in avoidSet,
8262 //           - in elemSet provided that !elemSet.empty()
8263 //           i1 and i2 optionally returns indices of n1 and n2
8264 //=======================================================================
8265
8266 const SMDS_MeshElement*
8267 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8268                                 const SMDS_MeshNode*    n2,
8269                                 const TIDSortedElemSet& elemSet,
8270                                 const TIDSortedElemSet& avoidSet,
8271                                 int*                    n1ind,
8272                                 int*                    n2ind)
8273
8274 {
8275   int i1, i2;
8276   const SMDS_MeshElement* face = 0;
8277
8278   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8279   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8280   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8281   {
8282     //MESSAGE("in while ( invElemIt->more() && !face )");
8283     const SMDS_MeshElement* elem = invElemIt->next();
8284     if (avoidSet.count( elem ))
8285       continue;
8286     if ( !elemSet.empty() && !elemSet.count( elem ))
8287       continue;
8288     // index of n1
8289     i1 = elem->GetNodeIndex( n1 );
8290     // find a n2 linked to n1
8291     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8292     for ( int di = -1; di < 2 && !face; di += 2 )
8293     {
8294       i2 = (i1+di+nbN) % nbN;
8295       if ( elem->GetNode( i2 ) == n2 )
8296         face = elem;
8297     }
8298     if ( !face && elem->IsQuadratic())
8299     {
8300       // analysis for quadratic elements using all nodes
8301       const SMDS_VtkFace* F =
8302         dynamic_cast<const SMDS_VtkFace*>(elem);
8303       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8304       // use special nodes iterator
8305       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8306       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8307       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8308       {
8309         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8310         if ( n1 == prevN && n2 == n )
8311         {
8312           face = elem;
8313         }
8314         else if ( n2 == prevN && n1 == n )
8315         {
8316           face = elem; swap( i1, i2 );
8317         }
8318         prevN = n;
8319       }
8320     }
8321   }
8322   if ( n1ind ) *n1ind = i1;
8323   if ( n2ind ) *n2ind = i2;
8324   return face;
8325 }
8326
8327 //=======================================================================
8328 //function : findAdjacentFace
8329 //purpose  :
8330 //=======================================================================
8331
8332 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8333                                                 const SMDS_MeshNode* n2,
8334                                                 const SMDS_MeshElement* elem)
8335 {
8336   TIDSortedElemSet elemSet, avoidSet;
8337   if ( elem )
8338     avoidSet.insert ( elem );
8339   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8340 }
8341
8342 //=======================================================================
8343 //function : FindFreeBorder
8344 //purpose  :
8345 //=======================================================================
8346
8347 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8348
8349 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8350                                        const SMDS_MeshNode*             theSecondNode,
8351                                        const SMDS_MeshNode*             theLastNode,
8352                                        list< const SMDS_MeshNode* > &   theNodes,
8353                                        list< const SMDS_MeshElement* >& theFaces)
8354 {
8355   if ( !theFirstNode || !theSecondNode )
8356     return false;
8357   // find border face between theFirstNode and theSecondNode
8358   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8359   if ( !curElem )
8360     return false;
8361
8362   theFaces.push_back( curElem );
8363   theNodes.push_back( theFirstNode );
8364   theNodes.push_back( theSecondNode );
8365
8366   //vector<const SMDS_MeshNode*> nodes;
8367   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8368   TIDSortedElemSet foundElems;
8369   bool needTheLast = ( theLastNode != 0 );
8370
8371   while ( nStart != theLastNode ) {
8372     if ( nStart == theFirstNode )
8373       return !needTheLast;
8374
8375     // find all free border faces sharing form nStart
8376
8377     list< const SMDS_MeshElement* > curElemList;
8378     list< const SMDS_MeshNode* > nStartList;
8379     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8380     while ( invElemIt->more() ) {
8381       const SMDS_MeshElement* e = invElemIt->next();
8382       if ( e == curElem || foundElems.insert( e ).second ) {
8383         // get nodes
8384         int iNode = 0, nbNodes = e->NbNodes();
8385         //const SMDS_MeshNode* nodes[nbNodes+1];
8386         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8387
8388         if(e->IsQuadratic()) {
8389           const SMDS_VtkFace* F =
8390             dynamic_cast<const SMDS_VtkFace*>(e);
8391           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8392           // use special nodes iterator
8393           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8394           while( anIter->more() ) {
8395             nodes[ iNode++ ] = cast2Node(anIter->next());
8396           }
8397         }
8398         else {
8399           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8400           while ( nIt->more() )
8401             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8402         }
8403         nodes[ iNode ] = nodes[ 0 ];
8404         // check 2 links
8405         for ( iNode = 0; iNode < nbNodes; iNode++ )
8406           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8407                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8408               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8409           {
8410             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8411             curElemList.push_back( e );
8412           }
8413       }
8414     }
8415     // analyse the found
8416
8417     int nbNewBorders = curElemList.size();
8418     if ( nbNewBorders == 0 ) {
8419       // no free border furthermore
8420       return !needTheLast;
8421     }
8422     else if ( nbNewBorders == 1 ) {
8423       // one more element found
8424       nIgnore = nStart;
8425       nStart = nStartList.front();
8426       curElem = curElemList.front();
8427       theFaces.push_back( curElem );
8428       theNodes.push_back( nStart );
8429     }
8430     else {
8431       // several continuations found
8432       list< const SMDS_MeshElement* >::iterator curElemIt;
8433       list< const SMDS_MeshNode* >::iterator nStartIt;
8434       // check if one of them reached the last node
8435       if ( needTheLast ) {
8436         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8437              curElemIt!= curElemList.end();
8438              curElemIt++, nStartIt++ )
8439           if ( *nStartIt == theLastNode ) {
8440             theFaces.push_back( *curElemIt );
8441             theNodes.push_back( *nStartIt );
8442             return true;
8443           }
8444       }
8445       // find the best free border by the continuations
8446       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8447       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8448       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8449            curElemIt!= curElemList.end();
8450            curElemIt++, nStartIt++ )
8451       {
8452         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8453         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8454         // find one more free border
8455         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8456           cNL->clear();
8457           cFL->clear();
8458         }
8459         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8460           // choice: clear a worse one
8461           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8462           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8463           contNodes[ iWorse ].clear();
8464           contFaces[ iWorse ].clear();
8465         }
8466       }
8467       if ( contNodes[0].empty() && contNodes[1].empty() )
8468         return false;
8469
8470       // append the best free border
8471       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8472       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8473       theNodes.pop_back(); // remove nIgnore
8474       theNodes.pop_back(); // remove nStart
8475       theFaces.pop_back(); // remove curElem
8476       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8477       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8478       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8479       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8480       return true;
8481
8482     } // several continuations found
8483   } // while ( nStart != theLastNode )
8484
8485   return true;
8486 }
8487
8488 //=======================================================================
8489 //function : CheckFreeBorderNodes
8490 //purpose  : Return true if the tree nodes are on a free border
8491 //=======================================================================
8492
8493 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8494                                             const SMDS_MeshNode* theNode2,
8495                                             const SMDS_MeshNode* theNode3)
8496 {
8497   list< const SMDS_MeshNode* > nodes;
8498   list< const SMDS_MeshElement* > faces;
8499   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8500 }
8501
8502 //=======================================================================
8503 //function : SewFreeBorder
8504 //purpose  :
8505 //=======================================================================
8506
8507 SMESH_MeshEditor::Sew_Error
8508 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8509                                  const SMDS_MeshNode* theBordSecondNode,
8510                                  const SMDS_MeshNode* theBordLastNode,
8511                                  const SMDS_MeshNode* theSideFirstNode,
8512                                  const SMDS_MeshNode* theSideSecondNode,
8513                                  const SMDS_MeshNode* theSideThirdNode,
8514                                  const bool           theSideIsFreeBorder,
8515                                  const bool           toCreatePolygons,
8516                                  const bool           toCreatePolyedrs)
8517 {
8518   myLastCreatedElems.Clear();
8519   myLastCreatedNodes.Clear();
8520
8521   MESSAGE("::SewFreeBorder()");
8522   Sew_Error aResult = SEW_OK;
8523
8524   // ====================================
8525   //    find side nodes and elements
8526   // ====================================
8527
8528   list< const SMDS_MeshNode* > nSide[ 2 ];
8529   list< const SMDS_MeshElement* > eSide[ 2 ];
8530   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8531   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8532
8533   // Free border 1
8534   // --------------
8535   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8536                       nSide[0], eSide[0])) {
8537     MESSAGE(" Free Border 1 not found " );
8538     aResult = SEW_BORDER1_NOT_FOUND;
8539   }
8540   if (theSideIsFreeBorder) {
8541     // Free border 2
8542     // --------------
8543     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8544                         nSide[1], eSide[1])) {
8545       MESSAGE(" Free Border 2 not found " );
8546       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8547     }
8548   }
8549   if ( aResult != SEW_OK )
8550     return aResult;
8551
8552   if (!theSideIsFreeBorder) {
8553     // Side 2
8554     // --------------
8555
8556     // -------------------------------------------------------------------------
8557     // Algo:
8558     // 1. If nodes to merge are not coincident, move nodes of the free border
8559     //    from the coord sys defined by the direction from the first to last
8560     //    nodes of the border to the correspondent sys of the side 2
8561     // 2. On the side 2, find the links most co-directed with the correspondent
8562     //    links of the free border
8563     // -------------------------------------------------------------------------
8564
8565     // 1. Since sewing may break if there are volumes to split on the side 2,
8566     //    we wont move nodes but just compute new coordinates for them
8567     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8568     TNodeXYZMap nBordXYZ;
8569     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8570     list< const SMDS_MeshNode* >::iterator nBordIt;
8571
8572     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8573     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8574     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8575     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8576     double tol2 = 1.e-8;
8577     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8578     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8579       // Need node movement.
8580
8581       // find X and Z axes to create trsf
8582       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8583       gp_Vec X = Zs ^ Zb;
8584       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8585         // Zb || Zs
8586         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8587
8588       // coord systems
8589       gp_Ax3 toBordAx( Pb1, Zb, X );
8590       gp_Ax3 fromSideAx( Ps1, Zs, X );
8591       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8592       // set trsf
8593       gp_Trsf toBordSys, fromSide2Sys;
8594       toBordSys.SetTransformation( toBordAx );
8595       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8596       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8597
8598       // move
8599       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8600         const SMDS_MeshNode* n = *nBordIt;
8601         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8602         toBordSys.Transforms( xyz );
8603         fromSide2Sys.Transforms( xyz );
8604         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8605       }
8606     }
8607     else {
8608       // just insert nodes XYZ in the nBordXYZ map
8609       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8610         const SMDS_MeshNode* n = *nBordIt;
8611         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8612       }
8613     }
8614
8615     // 2. On the side 2, find the links most co-directed with the correspondent
8616     //    links of the free border
8617
8618     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8619     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8620     sideNodes.push_back( theSideFirstNode );
8621
8622     bool hasVolumes = false;
8623     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8624     set<long> foundSideLinkIDs, checkedLinkIDs;
8625     SMDS_VolumeTool volume;
8626     //const SMDS_MeshNode* faceNodes[ 4 ];
8627
8628     const SMDS_MeshNode*    sideNode;
8629     const SMDS_MeshElement* sideElem;
8630     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8631     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8632     nBordIt = bordNodes.begin();
8633     nBordIt++;
8634     // border node position and border link direction to compare with
8635     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8636     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8637     // choose next side node by link direction or by closeness to
8638     // the current border node:
8639     bool searchByDir = ( *nBordIt != theBordLastNode );
8640     do {
8641       // find the next node on the Side 2
8642       sideNode = 0;
8643       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8644       long linkID;
8645       checkedLinkIDs.clear();
8646       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8647
8648       // loop on inverse elements of current node (prevSideNode) on the Side 2
8649       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8650       while ( invElemIt->more() )
8651       {
8652         const SMDS_MeshElement* elem = invElemIt->next();
8653         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8654         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8655         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8656         bool isVolume = volume.Set( elem );
8657         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8658         if ( isVolume ) // --volume
8659           hasVolumes = true;
8660         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8661           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8662           if(elem->IsQuadratic()) {
8663             const SMDS_VtkFace* F =
8664               dynamic_cast<const SMDS_VtkFace*>(elem);
8665             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8666             // use special nodes iterator
8667             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8668             while( anIter->more() ) {
8669               nodes[ iNode ] = cast2Node(anIter->next());
8670               if ( nodes[ iNode++ ] == prevSideNode )
8671                 iPrevNode = iNode - 1;
8672             }
8673           }
8674           else {
8675             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8676             while ( nIt->more() ) {
8677               nodes[ iNode ] = cast2Node( nIt->next() );
8678               if ( nodes[ iNode++ ] == prevSideNode )
8679                 iPrevNode = iNode - 1;
8680             }
8681           }
8682           // there are 2 links to check
8683           nbNodes = 2;
8684         }
8685         else // --edge
8686           continue;
8687         // loop on links, to be precise, on the second node of links
8688         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8689           const SMDS_MeshNode* n = nodes[ iNode ];
8690           if ( isVolume ) {
8691             if ( !volume.IsLinked( n, prevSideNode ))
8692               continue;
8693           }
8694           else {
8695             if ( iNode ) // a node before prevSideNode
8696               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8697             else         // a node after prevSideNode
8698               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8699           }
8700           // check if this link was already used
8701           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8702           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8703           if (!isJustChecked &&
8704               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8705           {
8706             // test a link geometrically
8707             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8708             bool linkIsBetter = false;
8709             double dot = 0.0, dist = 0.0;
8710             if ( searchByDir ) { // choose most co-directed link
8711               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8712               linkIsBetter = ( dot > maxDot );
8713             }
8714             else { // choose link with the node closest to bordPos
8715               dist = ( nextXYZ - bordPos ).SquareModulus();
8716               linkIsBetter = ( dist < minDist );
8717             }
8718             if ( linkIsBetter ) {
8719               maxDot = dot;
8720               minDist = dist;
8721               linkID = iLink;
8722               sideNode = n;
8723               sideElem = elem;
8724             }
8725           }
8726         }
8727       } // loop on inverse elements of prevSideNode
8728
8729       if ( !sideNode ) {
8730         MESSAGE(" Cant find path by links of the Side 2 ");
8731         return SEW_BAD_SIDE_NODES;
8732       }
8733       sideNodes.push_back( sideNode );
8734       sideElems.push_back( sideElem );
8735       foundSideLinkIDs.insert ( linkID );
8736       prevSideNode = sideNode;
8737
8738       if ( *nBordIt == theBordLastNode )
8739         searchByDir = false;
8740       else {
8741         // find the next border link to compare with
8742         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8743         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8744         // move to next border node if sideNode is before forward border node (bordPos)
8745         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8746           prevBordNode = *nBordIt;
8747           nBordIt++;
8748           bordPos = nBordXYZ[ *nBordIt ];
8749           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8750           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8751         }
8752       }
8753     }
8754     while ( sideNode != theSideSecondNode );
8755
8756     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8757       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8758       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8759     }
8760   } // end nodes search on the side 2
8761
8762   // ============================
8763   // sew the border to the side 2
8764   // ============================
8765
8766   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8767   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8768
8769   TListOfListOfNodes nodeGroupsToMerge;
8770   if ( nbNodes[0] == nbNodes[1] ||
8771        ( theSideIsFreeBorder && !theSideThirdNode)) {
8772
8773     // all nodes are to be merged
8774
8775     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8776          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8777          nIt[0]++, nIt[1]++ )
8778     {
8779       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8780       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8781       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8782     }
8783   }
8784   else {
8785
8786     // insert new nodes into the border and the side to get equal nb of segments
8787
8788     // get normalized parameters of nodes on the borders
8789     //double param[ 2 ][ maxNbNodes ];
8790     double* param[ 2 ];
8791     param[0] = new double [ maxNbNodes ];
8792     param[1] = new double [ maxNbNodes ];
8793     int iNode, iBord;
8794     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8795       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8796       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8797       const SMDS_MeshNode* nPrev = *nIt;
8798       double bordLength = 0;
8799       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8800         const SMDS_MeshNode* nCur = *nIt;
8801         gp_XYZ segment (nCur->X() - nPrev->X(),
8802                         nCur->Y() - nPrev->Y(),
8803                         nCur->Z() - nPrev->Z());
8804         double segmentLen = segment.Modulus();
8805         bordLength += segmentLen;
8806         param[ iBord ][ iNode ] = bordLength;
8807         nPrev = nCur;
8808       }
8809       // normalize within [0,1]
8810       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8811         param[ iBord ][ iNode ] /= bordLength;
8812       }
8813     }
8814
8815     // loop on border segments
8816     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8817     int i[ 2 ] = { 0, 0 };
8818     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8819     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8820
8821     TElemOfNodeListMap insertMap;
8822     TElemOfNodeListMap::iterator insertMapIt;
8823     // insertMap is
8824     // key:   elem to insert nodes into
8825     // value: 2 nodes to insert between + nodes to be inserted
8826     do {
8827       bool next[ 2 ] = { false, false };
8828
8829       // find min adjacent segment length after sewing
8830       double nextParam = 10., prevParam = 0;
8831       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8832         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8833           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8834         if ( i[ iBord ] > 0 )
8835           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8836       }
8837       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8838       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8839       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8840
8841       // choose to insert or to merge nodes
8842       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8843       if ( Abs( du ) <= minSegLen * 0.2 ) {
8844         // merge
8845         // ------
8846         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8847         const SMDS_MeshNode* n0 = *nIt[0];
8848         const SMDS_MeshNode* n1 = *nIt[1];
8849         nodeGroupsToMerge.back().push_back( n1 );
8850         nodeGroupsToMerge.back().push_back( n0 );
8851         // position of node of the border changes due to merge
8852         param[ 0 ][ i[0] ] += du;
8853         // move n1 for the sake of elem shape evaluation during insertion.
8854         // n1 will be removed by MergeNodes() anyway
8855         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8856         next[0] = next[1] = true;
8857       }
8858       else {
8859         // insert
8860         // ------
8861         int intoBord = ( du < 0 ) ? 0 : 1;
8862         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8863         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8864         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8865         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8866         if ( intoBord == 1 ) {
8867           // move node of the border to be on a link of elem of the side
8868           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8869           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8870           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8871           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8872           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8873         }
8874         insertMapIt = insertMap.find( elem );
8875         bool notFound = ( insertMapIt == insertMap.end() );
8876         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8877         if ( otherLink ) {
8878           // insert into another link of the same element:
8879           // 1. perform insertion into the other link of the elem
8880           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8881           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8882           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8883           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8884           // 2. perform insertion into the link of adjacent faces
8885           while (true) {
8886             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8887             if ( adjElem )
8888               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8889             else
8890               break;
8891           }
8892           if (toCreatePolyedrs) {
8893             // perform insertion into the links of adjacent volumes
8894             UpdateVolumes(n12, n22, nodeList);
8895           }
8896           // 3. find an element appeared on n1 and n2 after the insertion
8897           insertMap.erase( elem );
8898           elem = findAdjacentFace( n1, n2, 0 );
8899         }
8900         if ( notFound || otherLink ) {
8901           // add element and nodes of the side into the insertMap
8902           insertMapIt = insertMap.insert
8903             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8904           (*insertMapIt).second.push_back( n1 );
8905           (*insertMapIt).second.push_back( n2 );
8906         }
8907         // add node to be inserted into elem
8908         (*insertMapIt).second.push_back( nIns );
8909         next[ 1 - intoBord ] = true;
8910       }
8911
8912       // go to the next segment
8913       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8914         if ( next[ iBord ] ) {
8915           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8916             eIt[ iBord ]++;
8917           nPrev[ iBord ] = *nIt[ iBord ];
8918           nIt[ iBord ]++; i[ iBord ]++;
8919         }
8920       }
8921     }
8922     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8923
8924     // perform insertion of nodes into elements
8925
8926     for (insertMapIt = insertMap.begin();
8927          insertMapIt != insertMap.end();
8928          insertMapIt++ )
8929     {
8930       const SMDS_MeshElement* elem = (*insertMapIt).first;
8931       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8932       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8933       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8934
8935       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8936
8937       if ( !theSideIsFreeBorder ) {
8938         // look for and insert nodes into the faces adjacent to elem
8939         while (true) {
8940           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8941           if ( adjElem )
8942             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8943           else
8944             break;
8945         }
8946       }
8947       if (toCreatePolyedrs) {
8948         // perform insertion into the links of adjacent volumes
8949         UpdateVolumes(n1, n2, nodeList);
8950       }
8951     }
8952
8953     delete param[0];
8954     delete param[1];
8955   } // end: insert new nodes
8956
8957   MergeNodes ( nodeGroupsToMerge );
8958
8959   return aResult;
8960 }
8961
8962 //=======================================================================
8963 //function : InsertNodesIntoLink
8964 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8965 //           and theBetweenNode2 and split theElement
8966 //=======================================================================
8967
8968 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8969                                            const SMDS_MeshNode*        theBetweenNode1,
8970                                            const SMDS_MeshNode*        theBetweenNode2,
8971                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8972                                            const bool                  toCreatePoly)
8973 {
8974   if ( theFace->GetType() != SMDSAbs_Face ) return;
8975
8976   // find indices of 2 link nodes and of the rest nodes
8977   int iNode = 0, il1, il2, i3, i4;
8978   il1 = il2 = i3 = i4 = -1;
8979   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8980   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8981
8982   if(theFace->IsQuadratic()) {
8983     const SMDS_VtkFace* F =
8984       dynamic_cast<const SMDS_VtkFace*>(theFace);
8985     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8986     // use special nodes iterator
8987     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8988     while( anIter->more() ) {
8989       const SMDS_MeshNode* n = cast2Node(anIter->next());
8990       if ( n == theBetweenNode1 )
8991         il1 = iNode;
8992       else if ( n == theBetweenNode2 )
8993         il2 = iNode;
8994       else if ( i3 < 0 )
8995         i3 = iNode;
8996       else
8997         i4 = iNode;
8998       nodes[ iNode++ ] = n;
8999     }
9000   }
9001   else {
9002     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9003     while ( nodeIt->more() ) {
9004       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9005       if ( n == theBetweenNode1 )
9006         il1 = iNode;
9007       else if ( n == theBetweenNode2 )
9008         il2 = iNode;
9009       else if ( i3 < 0 )
9010         i3 = iNode;
9011       else
9012         i4 = iNode;
9013       nodes[ iNode++ ] = n;
9014     }
9015   }
9016   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9017     return ;
9018
9019   // arrange link nodes to go one after another regarding the face orientation
9020   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9021   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9022   if ( reverse ) {
9023     iNode = il1;
9024     il1 = il2;
9025     il2 = iNode;
9026     aNodesToInsert.reverse();
9027   }
9028   // check that not link nodes of a quadrangles are in good order
9029   int nbFaceNodes = theFace->NbNodes();
9030   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9031     iNode = i3;
9032     i3 = i4;
9033     i4 = iNode;
9034   }
9035
9036   if (toCreatePoly || theFace->IsPoly()) {
9037
9038     iNode = 0;
9039     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9040
9041     // add nodes of face up to first node of link
9042     bool isFLN = false;
9043
9044     if(theFace->IsQuadratic()) {
9045       const SMDS_VtkFace* F =
9046         dynamic_cast<const SMDS_VtkFace*>(theFace);
9047       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9048       // use special nodes iterator
9049       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9050       while( anIter->more()  && !isFLN ) {
9051         const SMDS_MeshNode* n = cast2Node(anIter->next());
9052         poly_nodes[iNode++] = n;
9053         if (n == nodes[il1]) {
9054           isFLN = true;
9055         }
9056       }
9057       // add nodes to insert
9058       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9059       for (; nIt != aNodesToInsert.end(); nIt++) {
9060         poly_nodes[iNode++] = *nIt;
9061       }
9062       // add nodes of face starting from last node of link
9063       while ( anIter->more() ) {
9064         poly_nodes[iNode++] = cast2Node(anIter->next());
9065       }
9066     }
9067     else {
9068       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9069       while ( nodeIt->more() && !isFLN ) {
9070         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9071         poly_nodes[iNode++] = n;
9072         if (n == nodes[il1]) {
9073           isFLN = true;
9074         }
9075       }
9076       // add nodes to insert
9077       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9078       for (; nIt != aNodesToInsert.end(); nIt++) {
9079         poly_nodes[iNode++] = *nIt;
9080       }
9081       // add nodes of face starting from last node of link
9082       while ( nodeIt->more() ) {
9083         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9084         poly_nodes[iNode++] = n;
9085       }
9086     }
9087
9088     // edit or replace the face
9089     SMESHDS_Mesh *aMesh = GetMeshDS();
9090
9091     if (theFace->IsPoly()) {
9092       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9093     }
9094     else {
9095       int aShapeId = FindShape( theFace );
9096
9097       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9098       myLastCreatedElems.Append(newElem);
9099       if ( aShapeId && newElem )
9100         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9101
9102       aMesh->RemoveElement(theFace);
9103     }
9104     return;
9105   }
9106
9107   SMESHDS_Mesh *aMesh = GetMeshDS();
9108   if( !theFace->IsQuadratic() ) {
9109
9110     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9111     int nbLinkNodes = 2 + aNodesToInsert.size();
9112     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9113     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9114     linkNodes[ 0 ] = nodes[ il1 ];
9115     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9116     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9117     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9118       linkNodes[ iNode++ ] = *nIt;
9119     }
9120     // decide how to split a quadrangle: compare possible variants
9121     // and choose which of splits to be a quadrangle
9122     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9123     if ( nbFaceNodes == 3 ) {
9124       iBestQuad = nbSplits;
9125       i4 = i3;
9126     }
9127     else if ( nbFaceNodes == 4 ) {
9128       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9129       double aBestRate = DBL_MAX;
9130       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9131         i1 = 0; i2 = 1;
9132         double aBadRate = 0;
9133         // evaluate elements quality
9134         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9135           if ( iSplit == iQuad ) {
9136             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9137                                    linkNodes[ i2++ ],
9138                                    nodes[ i3 ],
9139                                    nodes[ i4 ]);
9140             aBadRate += getBadRate( &quad, aCrit );
9141           }
9142           else {
9143             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9144                                    linkNodes[ i2++ ],
9145                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9146             aBadRate += getBadRate( &tria, aCrit );
9147           }
9148         }
9149         // choice
9150         if ( aBadRate < aBestRate ) {
9151           iBestQuad = iQuad;
9152           aBestRate = aBadRate;
9153         }
9154       }
9155     }
9156
9157     // create new elements
9158     int aShapeId = FindShape( theFace );
9159
9160     i1 = 0; i2 = 1;
9161     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9162       SMDS_MeshElement* newElem = 0;
9163       if ( iSplit == iBestQuad )
9164         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9165                                   linkNodes[ i2++ ],
9166                                   nodes[ i3 ],
9167                                   nodes[ i4 ]);
9168       else
9169         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9170                                   linkNodes[ i2++ ],
9171                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9172       myLastCreatedElems.Append(newElem);
9173       if ( aShapeId && newElem )
9174         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9175     }
9176
9177     // change nodes of theFace
9178     const SMDS_MeshNode* newNodes[ 4 ];
9179     newNodes[ 0 ] = linkNodes[ i1 ];
9180     newNodes[ 1 ] = linkNodes[ i2 ];
9181     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9182     newNodes[ 3 ] = nodes[ i4 ];
9183     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9184     const SMDS_MeshElement* newElem = 0;
9185     if (iSplit == iBestQuad)
9186       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9187     else
9188       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9189     myLastCreatedElems.Append(newElem);
9190     if ( aShapeId && newElem )
9191       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9192 } // end if(!theFace->IsQuadratic())
9193   else { // theFace is quadratic
9194     // we have to split theFace on simple triangles and one simple quadrangle
9195     int tmp = il1/2;
9196     int nbshift = tmp*2;
9197     // shift nodes in nodes[] by nbshift
9198     int i,j;
9199     for(i=0; i<nbshift; i++) {
9200       const SMDS_MeshNode* n = nodes[0];
9201       for(j=0; j<nbFaceNodes-1; j++) {
9202         nodes[j] = nodes[j+1];
9203       }
9204       nodes[nbFaceNodes-1] = n;
9205     }
9206     il1 = il1 - nbshift;
9207     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9208     //   n0      n1     n2    n0      n1     n2
9209     //     +-----+-----+        +-----+-----+
9210     //      \         /         |           |
9211     //       \       /          |           |
9212     //      n5+     +n3       n7+           +n3
9213     //         \   /            |           |
9214     //          \ /             |           |
9215     //           +              +-----+-----+
9216     //           n4           n6      n5     n4
9217
9218     // create new elements
9219     int aShapeId = FindShape( theFace );
9220
9221     int n1,n2,n3;
9222     if(nbFaceNodes==6) { // quadratic triangle
9223       SMDS_MeshElement* newElem =
9224         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9225       myLastCreatedElems.Append(newElem);
9226       if ( aShapeId && newElem )
9227         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9228       if(theFace->IsMediumNode(nodes[il1])) {
9229         // create quadrangle
9230         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9231         myLastCreatedElems.Append(newElem);
9232         if ( aShapeId && newElem )
9233           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9234         n1 = 1;
9235         n2 = 2;
9236         n3 = 3;
9237       }
9238       else {
9239         // create quadrangle
9240         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9241         myLastCreatedElems.Append(newElem);
9242         if ( aShapeId && newElem )
9243           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9244         n1 = 0;
9245         n2 = 1;
9246         n3 = 5;
9247       }
9248     }
9249     else { // nbFaceNodes==8 - quadratic quadrangle
9250       SMDS_MeshElement* newElem =
9251         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9252       myLastCreatedElems.Append(newElem);
9253       if ( aShapeId && newElem )
9254         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9255       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9256       myLastCreatedElems.Append(newElem);
9257       if ( aShapeId && newElem )
9258         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9259       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9260       myLastCreatedElems.Append(newElem);
9261       if ( aShapeId && newElem )
9262         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9263       if(theFace->IsMediumNode(nodes[il1])) {
9264         // create quadrangle
9265         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9266         myLastCreatedElems.Append(newElem);
9267         if ( aShapeId && newElem )
9268           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9269         n1 = 1;
9270         n2 = 2;
9271         n3 = 3;
9272       }
9273       else {
9274         // create quadrangle
9275         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9276         myLastCreatedElems.Append(newElem);
9277         if ( aShapeId && newElem )
9278           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9279         n1 = 0;
9280         n2 = 1;
9281         n3 = 7;
9282       }
9283     }
9284     // create needed triangles using n1,n2,n3 and inserted nodes
9285     int nbn = 2 + aNodesToInsert.size();
9286     //const SMDS_MeshNode* aNodes[nbn];
9287     vector<const SMDS_MeshNode*> aNodes(nbn);
9288     aNodes[0] = nodes[n1];
9289     aNodes[nbn-1] = nodes[n2];
9290     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9291     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9292       aNodes[iNode++] = *nIt;
9293     }
9294     for(i=1; i<nbn; i++) {
9295       SMDS_MeshElement* newElem =
9296         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9297       myLastCreatedElems.Append(newElem);
9298       if ( aShapeId && newElem )
9299         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9300     }
9301   }
9302   // remove old face
9303   aMesh->RemoveElement(theFace);
9304 }
9305
9306 //=======================================================================
9307 //function : UpdateVolumes
9308 //purpose  :
9309 //=======================================================================
9310 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9311                                       const SMDS_MeshNode*        theBetweenNode2,
9312                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9313 {
9314   myLastCreatedElems.Clear();
9315   myLastCreatedNodes.Clear();
9316
9317   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9318   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9319     const SMDS_MeshElement* elem = invElemIt->next();
9320
9321     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9322     SMDS_VolumeTool aVolume (elem);
9323     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9324       continue;
9325
9326     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9327     int iface, nbFaces = aVolume.NbFaces();
9328     vector<const SMDS_MeshNode *> poly_nodes;
9329     vector<int> quantities (nbFaces);
9330
9331     for (iface = 0; iface < nbFaces; iface++) {
9332       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9333       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9334       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9335
9336       for (int inode = 0; inode < nbFaceNodes; inode++) {
9337         poly_nodes.push_back(faceNodes[inode]);
9338
9339         if (nbInserted == 0) {
9340           if (faceNodes[inode] == theBetweenNode1) {
9341             if (faceNodes[inode + 1] == theBetweenNode2) {
9342               nbInserted = theNodesToInsert.size();
9343
9344               // add nodes to insert
9345               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9346               for (; nIt != theNodesToInsert.end(); nIt++) {
9347                 poly_nodes.push_back(*nIt);
9348               }
9349             }
9350           }
9351           else if (faceNodes[inode] == theBetweenNode2) {
9352             if (faceNodes[inode + 1] == theBetweenNode1) {
9353               nbInserted = theNodesToInsert.size();
9354
9355               // add nodes to insert in reversed order
9356               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9357               nIt--;
9358               for (; nIt != theNodesToInsert.begin(); nIt--) {
9359                 poly_nodes.push_back(*nIt);
9360               }
9361               poly_nodes.push_back(*nIt);
9362             }
9363           }
9364           else {
9365           }
9366         }
9367       }
9368       quantities[iface] = nbFaceNodes + nbInserted;
9369     }
9370
9371     // Replace or update the volume
9372     SMESHDS_Mesh *aMesh = GetMeshDS();
9373
9374     if (elem->IsPoly()) {
9375       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9376
9377     }
9378     else {
9379       int aShapeId = FindShape( elem );
9380
9381       SMDS_MeshElement* newElem =
9382         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9383       myLastCreatedElems.Append(newElem);
9384       if (aShapeId && newElem)
9385         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9386
9387       aMesh->RemoveElement(elem);
9388     }
9389   }
9390 }
9391
9392 namespace
9393 {
9394   //================================================================================
9395   /*!
9396    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9397    */
9398   //================================================================================
9399
9400   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9401                            vector<const SMDS_MeshNode *> & nodes,
9402                            vector<int> &                   nbNodeInFaces )
9403   {
9404     nodes.clear();
9405     nbNodeInFaces.clear();
9406     SMDS_VolumeTool vTool ( elem );
9407     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9408     {
9409       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9410       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9411       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9412     }
9413   }
9414 }
9415
9416 //=======================================================================
9417 /*!
9418  * \brief Convert elements contained in a submesh to quadratic
9419  * \return int - nb of checked elements
9420  */
9421 //=======================================================================
9422
9423 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9424                                              SMESH_MesherHelper& theHelper,
9425                                              const bool          theForce3d)
9426 {
9427   int nbElem = 0;
9428   if( !theSm ) return nbElem;
9429
9430   vector<int> nbNodeInFaces;
9431   vector<const SMDS_MeshNode *> nodes;
9432   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9433   while(ElemItr->more())
9434   {
9435     nbElem++;
9436     const SMDS_MeshElement* elem = ElemItr->next();
9437     if( !elem || elem->IsQuadratic() ) continue;
9438
9439     // get elem data needed to re-create it
9440     //
9441     const int id                        = elem->GetID();
9442     const int nbNodes                   = elem->NbNodes();
9443     const SMDSAbs_ElementType aType     = elem->GetType();
9444     const SMDSAbs_EntityType  aGeomType = elem->GetEntityType();
9445     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9446     if ( aGeomType == SMDSEntity_Polyhedra )
9447       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9448     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9449       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9450
9451     // remove a linear element
9452     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9453
9454     const SMDS_MeshElement* NewElem = 0;
9455
9456     switch( aType )
9457     {
9458     case SMDSAbs_Edge :
9459       {
9460         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9461         break;
9462       }
9463     case SMDSAbs_Face :
9464       {
9465         switch(nbNodes)
9466         {
9467         case 3:
9468           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9469           break;
9470         case 4:
9471           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9472           break;
9473         default:
9474           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9475           continue;
9476         }
9477         break;
9478       }
9479     case SMDSAbs_Volume :
9480       {
9481         switch( aGeomType )
9482         {
9483         case SMDSEntity_Tetra:
9484           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9485           break;
9486         case SMDSEntity_Pyramid:
9487           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9488           break;
9489         case SMDSEntity_Penta:
9490           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9491           break;
9492         case SMDSEntity_Hexa:
9493           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9494                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9495           break;
9496         case SMDSEntity_Hexagonal_Prism:
9497         default:
9498           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9499         }
9500         break;
9501       }
9502     default :
9503       continue;
9504     }
9505     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9506     if( NewElem )
9507       theSm->AddElement( NewElem );
9508   }
9509   return nbElem;
9510 }
9511
9512 //=======================================================================
9513 //function : ConvertToQuadratic
9514 //purpose  :
9515 //=======================================================================
9516
9517 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9518 {
9519   SMESHDS_Mesh* meshDS = GetMeshDS();
9520
9521   SMESH_MesherHelper aHelper(*myMesh);
9522   aHelper.SetIsQuadratic( true );
9523
9524   int nbCheckedElems = 0;
9525   if ( myMesh->HasShapeToMesh() )
9526   {
9527     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9528     {
9529       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9530       while ( smIt->more() ) {
9531         SMESH_subMesh* sm = smIt->next();
9532         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9533           aHelper.SetSubShape( sm->GetSubShape() );
9534           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9535         }
9536       }
9537     }
9538   }
9539   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9540   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9541   {
9542     SMESHDS_SubMesh *smDS = 0;
9543     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9544     while(aEdgeItr->more())
9545     {
9546       const SMDS_MeshEdge* edge = aEdgeItr->next();
9547       if(edge && !edge->IsQuadratic())
9548       {
9549         int id = edge->GetID();
9550         //MESSAGE("edge->GetID() " << id);
9551         const SMDS_MeshNode* n1 = edge->GetNode(0);
9552         const SMDS_MeshNode* n2 = edge->GetNode(1);
9553
9554         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9555
9556         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9557         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9558       }
9559     }
9560     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9561     while(aFaceItr->more())
9562     {
9563       const SMDS_MeshFace* face = aFaceItr->next();
9564       if(!face || face->IsQuadratic() ) continue;
9565
9566       const int id = face->GetID();
9567       const SMDSAbs_EntityType type = face->GetEntityType();
9568       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9569
9570       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9571
9572       SMDS_MeshFace * NewFace = 0;
9573       switch( type )
9574       {
9575       case SMDSEntity_Triangle:
9576         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9577         break;
9578       case SMDSEntity_Quadrangle:
9579         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9580         break;
9581       default:
9582         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9583       }
9584       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9585     }
9586     vector<int> nbNodeInFaces;
9587     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9588     while(aVolumeItr->more())
9589     {
9590       const SMDS_MeshVolume* volume = aVolumeItr->next();
9591       if(!volume || volume->IsQuadratic() ) continue;
9592
9593       const int id = volume->GetID();
9594       const SMDSAbs_EntityType type = volume->GetEntityType();
9595       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9596       if ( type == SMDSEntity_Polyhedra )
9597         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9598       else if ( type == SMDSEntity_Hexagonal_Prism )
9599         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9600
9601       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9602
9603       SMDS_MeshVolume * NewVolume = 0;
9604       switch ( type )
9605       {
9606       case SMDSEntity_Tetra:
9607         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9608         break;
9609       case SMDSEntity_Hexa:
9610         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9611                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9612         break;
9613       case SMDSEntity_Pyramid:
9614         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9615                                       nodes[3], nodes[4], id, theForce3d);
9616         break;
9617       case SMDSEntity_Penta:
9618         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9619                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9620         break;
9621       case SMDSEntity_Hexagonal_Prism:
9622       default:
9623         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9624       }
9625       ReplaceElemInGroups(volume, NewVolume, meshDS);
9626     }
9627   }
9628
9629   if ( !theForce3d )
9630   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9631     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9632     aHelper.FixQuadraticElements();
9633   }
9634 }
9635
9636 //================================================================================
9637 /*!
9638  * \brief Makes given elements quadratic
9639  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9640  *  \param theElements - elements to make quadratic 
9641  */
9642 //================================================================================
9643
9644 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9645                                           TIDSortedElemSet& theElements)
9646 {
9647   if ( theElements.empty() ) return;
9648
9649   // we believe that all theElements are of the same type
9650   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9651   
9652   // get all nodes shared by theElements
9653   TIDSortedNodeSet allNodes;
9654   TIDSortedElemSet::iterator eIt = theElements.begin();
9655   for ( ; eIt != theElements.end(); ++eIt )
9656     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9657
9658   // complete theElements with elements of lower dim whose all nodes are in allNodes
9659
9660   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9661   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9662   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9663   for ( ; nIt != allNodes.end(); ++nIt )
9664   {
9665     const SMDS_MeshNode* n = *nIt;
9666     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9667     while ( invIt->more() )
9668     {
9669       const SMDS_MeshElement* e = invIt->next();
9670       if ( e->IsQuadratic() )
9671       {
9672         quadAdjacentElems[ e->GetType() ].insert( e );
9673         continue;
9674       }
9675       if ( e->GetType() >= elemType )
9676       {
9677         continue; // same type of more complex linear element
9678       }
9679
9680       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9681         continue; // e is already checked
9682
9683       // check nodes
9684       bool allIn = true;
9685       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9686       while ( nodeIt->more() && allIn )
9687         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9688       if ( allIn )
9689         theElements.insert(e );
9690     }
9691   }
9692
9693   SMESH_MesherHelper helper(*myMesh);
9694   helper.SetIsQuadratic( true );
9695
9696   // add links of quadratic adjacent elements to the helper
9697
9698   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9699     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9700           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9701     {
9702       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9703     }
9704   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9705     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9706           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9707     {
9708       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9709     }
9710   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9711     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9712           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9713     {
9714       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9715     }
9716
9717   // make quadratic elements instead of linear ones
9718
9719   SMESHDS_Mesh* meshDS = GetMeshDS();
9720   SMESHDS_SubMesh* smDS = 0;
9721   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9722   {
9723     const SMDS_MeshElement* elem = *eIt;
9724     if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9725       continue;
9726
9727     const int id                   = elem->GetID();
9728     const SMDSAbs_ElementType type = elem->GetType();
9729     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9730
9731     if ( !smDS || !smDS->Contains( elem ))
9732       smDS = meshDS->MeshElements( elem->getshapeId() );
9733     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9734
9735     SMDS_MeshElement * newElem = 0;
9736     switch( nodes.size() )
9737     {
9738     case 4: // cases for most frequently used element types go first (for optimization)
9739       if ( type == SMDSAbs_Volume )
9740         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9741       else
9742         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9743       break;
9744     case 8:
9745       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9746                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9747       break;
9748     case 3:
9749       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9750       break;
9751     case 2:
9752       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9753       break;
9754     case 5:
9755       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9756                                  nodes[4], id, theForce3d);
9757       break;
9758     case 6:
9759       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9760                                  nodes[4], nodes[5], id, theForce3d);
9761       break;
9762     default:;
9763     }
9764     ReplaceElemInGroups( elem, newElem, meshDS);
9765     if( newElem && smDS )
9766       smDS->AddElement( newElem );
9767   }
9768
9769   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9770   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9771     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9772     helper.FixQuadraticElements();
9773   }
9774 }
9775
9776 //=======================================================================
9777 /*!
9778  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9779  * \return int - nb of checked elements
9780  */
9781 //=======================================================================
9782
9783 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9784                                      SMDS_ElemIteratorPtr theItr,
9785                                      const int            theShapeID)
9786 {
9787   int nbElem = 0;
9788   SMESHDS_Mesh* meshDS = GetMeshDS();
9789
9790   while( theItr->more() )
9791   {
9792     const SMDS_MeshElement* elem = theItr->next();
9793     nbElem++;
9794     if( elem && elem->IsQuadratic())
9795     {
9796       int id                    = elem->GetID();
9797       int nbCornerNodes         = elem->NbCornerNodes();
9798       SMDSAbs_ElementType aType = elem->GetType();
9799
9800       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9801
9802       //remove a quadratic element
9803       if ( !theSm || !theSm->Contains( elem ))
9804         theSm = meshDS->MeshElements( elem->getshapeId() );
9805       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9806
9807       // remove medium nodes
9808       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9809         if ( nodes[i]->NbInverseElements() == 0 )
9810           meshDS->RemoveFreeNode( nodes[i], theSm );
9811
9812       // add a linear element
9813       nodes.resize( nbCornerNodes );
9814       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9815       ReplaceElemInGroups(elem, newElem, meshDS);
9816       if( theSm && newElem )
9817         theSm->AddElement( newElem );
9818     }
9819   }
9820   return nbElem;
9821 }
9822
9823 //=======================================================================
9824 //function : ConvertFromQuadratic
9825 //purpose  :
9826 //=======================================================================
9827
9828 bool SMESH_MeshEditor::ConvertFromQuadratic()
9829 {
9830   int nbCheckedElems = 0;
9831   if ( myMesh->HasShapeToMesh() )
9832   {
9833     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9834     {
9835       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9836       while ( smIt->more() ) {
9837         SMESH_subMesh* sm = smIt->next();
9838         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9839           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9840       }
9841     }
9842   }
9843
9844   int totalNbElems =
9845     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9846   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9847   {
9848     SMESHDS_SubMesh *aSM = 0;
9849     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9850   }
9851
9852   return true;
9853 }
9854
9855 namespace
9856 {
9857   //================================================================================
9858   /*!
9859    * \brief Return true if all medium nodes of the element are in the node set
9860    */
9861   //================================================================================
9862
9863   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9864   {
9865     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9866       if ( !nodeSet.count( elem->GetNode(i) ))
9867         return false;
9868     return true;
9869   }
9870 }
9871
9872 //================================================================================
9873 /*!
9874  * \brief Makes given elements linear
9875  */
9876 //================================================================================
9877
9878 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9879 {
9880   if ( theElements.empty() ) return;
9881
9882   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9883   set<int> mediumNodeIDs;
9884   TIDSortedElemSet::iterator eIt = theElements.begin();
9885   for ( ; eIt != theElements.end(); ++eIt )
9886   {
9887     const SMDS_MeshElement* e = *eIt;
9888     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9889       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9890   }
9891
9892   // replace given elements by linear ones
9893   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9894   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9895   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9896
9897   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9898   // except those elements sharing medium nodes of quadratic element whose medium nodes
9899   // are not all in mediumNodeIDs
9900
9901   // get remaining medium nodes
9902   TIDSortedNodeSet mediumNodes;
9903   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9904   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9905     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9906       mediumNodes.insert( mediumNodes.end(), n );
9907
9908   // find more quadratic elements to convert
9909   TIDSortedElemSet moreElemsToConvert;
9910   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9911   for ( ; nIt != mediumNodes.end(); ++nIt )
9912   {
9913     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9914     while ( invIt->more() )
9915     {
9916       const SMDS_MeshElement* e = invIt->next();
9917       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9918       {
9919         // find a more complex element including e and
9920         // whose medium nodes are not in mediumNodes
9921         bool complexFound = false;
9922         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9923         {
9924           SMDS_ElemIteratorPtr invIt2 =
9925             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9926           while ( invIt2->more() )
9927           {
9928             const SMDS_MeshElement* eComplex = invIt2->next();
9929             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9930             {
9931               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9932               if ( nbCommonNodes == e->NbNodes())
9933               {
9934                 complexFound = true;
9935                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9936                 break;
9937               }
9938             }
9939           }
9940         }
9941         if ( !complexFound )
9942           moreElemsToConvert.insert( e );
9943       }
9944     }
9945   }
9946   elemIt = SMDS_ElemIteratorPtr
9947     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9948   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9949 }
9950
9951 //=======================================================================
9952 //function : SewSideElements
9953 //purpose  :
9954 //=======================================================================
9955
9956 SMESH_MeshEditor::Sew_Error
9957 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9958                                    TIDSortedElemSet&    theSide2,
9959                                    const SMDS_MeshNode* theFirstNode1,
9960                                    const SMDS_MeshNode* theFirstNode2,
9961                                    const SMDS_MeshNode* theSecondNode1,
9962                                    const SMDS_MeshNode* theSecondNode2)
9963 {
9964   myLastCreatedElems.Clear();
9965   myLastCreatedNodes.Clear();
9966
9967   MESSAGE ("::::SewSideElements()");
9968   if ( theSide1.size() != theSide2.size() )
9969     return SEW_DIFF_NB_OF_ELEMENTS;
9970
9971   Sew_Error aResult = SEW_OK;
9972   // Algo:
9973   // 1. Build set of faces representing each side
9974   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9975   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9976
9977   // =======================================================================
9978   // 1. Build set of faces representing each side:
9979   // =======================================================================
9980   // a. build set of nodes belonging to faces
9981   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9982   // c. create temporary faces representing side of volumes if correspondent
9983   //    face does not exist
9984
9985   SMESHDS_Mesh* aMesh = GetMeshDS();
9986   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9987   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9988   TIDSortedElemSet             faceSet1, faceSet2;
9989   set<const SMDS_MeshElement*> volSet1,  volSet2;
9990   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9991   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9992   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9993   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9994   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9995   int iSide, iFace, iNode;
9996
9997   list<const SMDS_MeshElement* > tempFaceList;
9998   for ( iSide = 0; iSide < 2; iSide++ ) {
9999     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10000     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10001     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10002     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10003     set<const SMDS_MeshElement*>::iterator vIt;
10004     TIDSortedElemSet::iterator eIt;
10005     set<const SMDS_MeshNode*>::iterator    nIt;
10006
10007     // check that given nodes belong to given elements
10008     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10009     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10010     int firstIndex = -1, secondIndex = -1;
10011     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10012       const SMDS_MeshElement* elem = *eIt;
10013       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10014       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10015       if ( firstIndex > -1 && secondIndex > -1 ) break;
10016     }
10017     if ( firstIndex < 0 || secondIndex < 0 ) {
10018       // we can simply return until temporary faces created
10019       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10020     }
10021
10022     // -----------------------------------------------------------
10023     // 1a. Collect nodes of existing faces
10024     //     and build set of face nodes in order to detect missing
10025     //     faces corresponding to sides of volumes
10026     // -----------------------------------------------------------
10027
10028     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10029
10030     // loop on the given element of a side
10031     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10032       //const SMDS_MeshElement* elem = *eIt;
10033       const SMDS_MeshElement* elem = *eIt;
10034       if ( elem->GetType() == SMDSAbs_Face ) {
10035         faceSet->insert( elem );
10036         set <const SMDS_MeshNode*> faceNodeSet;
10037         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10038         while ( nodeIt->more() ) {
10039           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10040           nodeSet->insert( n );
10041           faceNodeSet.insert( n );
10042         }
10043         setOfFaceNodeSet.insert( faceNodeSet );
10044       }
10045       else if ( elem->GetType() == SMDSAbs_Volume )
10046         volSet->insert( elem );
10047     }
10048     // ------------------------------------------------------------------------------
10049     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10050     // ------------------------------------------------------------------------------
10051
10052     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10053       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10054       while ( fIt->more() ) { // loop on faces sharing a node
10055         const SMDS_MeshElement* f = fIt->next();
10056         if ( faceSet->find( f ) == faceSet->end() ) {
10057           // check if all nodes are in nodeSet and
10058           // complete setOfFaceNodeSet if they are
10059           set <const SMDS_MeshNode*> faceNodeSet;
10060           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10061           bool allInSet = true;
10062           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10063             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10064             if ( nodeSet->find( n ) == nodeSet->end() )
10065               allInSet = false;
10066             else
10067               faceNodeSet.insert( n );
10068           }
10069           if ( allInSet ) {
10070             faceSet->insert( f );
10071             setOfFaceNodeSet.insert( faceNodeSet );
10072           }
10073         }
10074       }
10075     }
10076
10077     // -------------------------------------------------------------------------
10078     // 1c. Create temporary faces representing sides of volumes if correspondent
10079     //     face does not exist
10080     // -------------------------------------------------------------------------
10081
10082     if ( !volSet->empty() ) {
10083       //int nodeSetSize = nodeSet->size();
10084
10085       // loop on given volumes
10086       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10087         SMDS_VolumeTool vol (*vIt);
10088         // loop on volume faces: find free faces
10089         // --------------------------------------
10090         list<const SMDS_MeshElement* > freeFaceList;
10091         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10092           if ( !vol.IsFreeFace( iFace ))
10093             continue;
10094           // check if there is already a face with same nodes in a face set
10095           const SMDS_MeshElement* aFreeFace = 0;
10096           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10097           int nbNodes = vol.NbFaceNodes( iFace );
10098           set <const SMDS_MeshNode*> faceNodeSet;
10099           vol.GetFaceNodes( iFace, faceNodeSet );
10100           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10101           if ( isNewFace ) {
10102             // no such a face is given but it still can exist, check it
10103             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10104             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10105           }
10106           if ( !aFreeFace ) {
10107             // create a temporary face
10108             if ( nbNodes == 3 ) {
10109               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10110               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10111             }
10112             else if ( nbNodes == 4 ) {
10113               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10114               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10115             }
10116             else {
10117               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10118               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10119               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10120             }
10121             if ( aFreeFace )
10122               tempFaceList.push_back( aFreeFace );
10123           }
10124
10125           if ( aFreeFace )
10126             freeFaceList.push_back( aFreeFace );
10127
10128         } // loop on faces of a volume
10129
10130         // choose one of several free faces of a volume
10131         // --------------------------------------------
10132         if ( freeFaceList.size() > 1 ) {
10133           // choose a face having max nb of nodes shared by other elems of a side
10134           int maxNbNodes = -1;
10135           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10136           while ( fIt != freeFaceList.end() ) { // loop on free faces
10137             int nbSharedNodes = 0;
10138             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10139             while ( nodeIt->more() ) { // loop on free face nodes
10140               const SMDS_MeshNode* n =
10141                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10142               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10143               while ( invElemIt->more() ) {
10144                 const SMDS_MeshElement* e = invElemIt->next();
10145                 nbSharedNodes += faceSet->count( e );
10146                 nbSharedNodes += elemSet->count( e );
10147               }
10148             }
10149             if ( nbSharedNodes > maxNbNodes ) {
10150               maxNbNodes = nbSharedNodes;
10151               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10152             }
10153             else if ( nbSharedNodes == maxNbNodes ) {
10154               fIt++;
10155             }
10156             else {
10157               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10158             }
10159           }
10160           if ( freeFaceList.size() > 1 )
10161           {
10162             // could not choose one face, use another way
10163             // choose a face most close to the bary center of the opposite side
10164             gp_XYZ aBC( 0., 0., 0. );
10165             set <const SMDS_MeshNode*> addedNodes;
10166             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10167             eIt = elemSet2->begin();
10168             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10169               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10170               while ( nodeIt->more() ) { // loop on free face nodes
10171                 const SMDS_MeshNode* n =
10172                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10173                 if ( addedNodes.insert( n ).second )
10174                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10175               }
10176             }
10177             aBC /= addedNodes.size();
10178             double minDist = DBL_MAX;
10179             fIt = freeFaceList.begin();
10180             while ( fIt != freeFaceList.end() ) { // loop on free faces
10181               double dist = 0;
10182               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10183               while ( nodeIt->more() ) { // loop on free face nodes
10184                 const SMDS_MeshNode* n =
10185                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10186                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10187                 dist += ( aBC - p ).SquareModulus();
10188               }
10189               if ( dist < minDist ) {
10190                 minDist = dist;
10191                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10192               }
10193               else
10194                 fIt = freeFaceList.erase( fIt++ );
10195             }
10196           }
10197         } // choose one of several free faces of a volume
10198
10199         if ( freeFaceList.size() == 1 ) {
10200           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10201           faceSet->insert( aFreeFace );
10202           // complete a node set with nodes of a found free face
10203           //           for ( iNode = 0; iNode < ; iNode++ )
10204           //             nodeSet->insert( fNodes[ iNode ] );
10205         }
10206
10207       } // loop on volumes of a side
10208
10209       //       // complete a set of faces if new nodes in a nodeSet appeared
10210       //       // ----------------------------------------------------------
10211       //       if ( nodeSetSize != nodeSet->size() ) {
10212       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10213       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10214       //           while ( fIt->more() ) { // loop on faces sharing a node
10215       //             const SMDS_MeshElement* f = fIt->next();
10216       //             if ( faceSet->find( f ) == faceSet->end() ) {
10217       //               // check if all nodes are in nodeSet and
10218       //               // complete setOfFaceNodeSet if they are
10219       //               set <const SMDS_MeshNode*> faceNodeSet;
10220       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10221       //               bool allInSet = true;
10222       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10223       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10224       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10225       //                   allInSet = false;
10226       //                 else
10227       //                   faceNodeSet.insert( n );
10228       //               }
10229       //               if ( allInSet ) {
10230       //                 faceSet->insert( f );
10231       //                 setOfFaceNodeSet.insert( faceNodeSet );
10232       //               }
10233       //             }
10234       //           }
10235       //         }
10236       //       }
10237     } // Create temporary faces, if there are volumes given
10238   } // loop on sides
10239
10240   if ( faceSet1.size() != faceSet2.size() ) {
10241     // delete temporary faces: they are in reverseElements of actual nodes
10242 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10243 //    while ( tmpFaceIt->more() )
10244 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10245 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10246 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10247 //      aMesh->RemoveElement(*tmpFaceIt);
10248     MESSAGE("Diff nb of faces");
10249     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10250   }
10251
10252   // ============================================================
10253   // 2. Find nodes to merge:
10254   //              bind a node to remove to a node to put instead
10255   // ============================================================
10256
10257   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10258   if ( theFirstNode1 != theFirstNode2 )
10259     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10260   if ( theSecondNode1 != theSecondNode2 )
10261     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10262
10263   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10264   set< long > linkIdSet; // links to process
10265   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10266
10267   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10268   list< NLink > linkList[2];
10269   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10270   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10271   // loop on links in linkList; find faces by links and append links
10272   // of the found faces to linkList
10273   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10274   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10275   {
10276     NLink link[] = { *linkIt[0], *linkIt[1] };
10277     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10278     if ( !linkIdSet.count( linkID ) )
10279       continue;
10280
10281     // by links, find faces in the face sets,
10282     // and find indices of link nodes in the found faces;
10283     // in a face set, there is only one or no face sharing a link
10284     // ---------------------------------------------------------------
10285
10286     const SMDS_MeshElement* face[] = { 0, 0 };
10287     vector<const SMDS_MeshNode*> fnodes[2];
10288     int iLinkNode[2][2];
10289     TIDSortedElemSet avoidSet;
10290     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10291       const SMDS_MeshNode* n1 = link[iSide].first;
10292       const SMDS_MeshNode* n2 = link[iSide].second;
10293       //cout << "Side " << iSide << " ";
10294       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10295       // find a face by two link nodes
10296       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10297                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10298       if ( face[ iSide ])
10299       {
10300         //cout << " F " << face[ iSide]->GetID() <<endl;
10301         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10302         // put face nodes to fnodes
10303         if ( face[ iSide ]->IsQuadratic() )
10304         {
10305           // use interlaced nodes iterator
10306           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10307           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10308           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10309           while ( nIter->more() )
10310             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10311         }
10312         else
10313         {
10314           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10315                                   face[ iSide ]->end_nodes() );
10316         }
10317         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10318       }
10319     }
10320
10321     // check similarity of elements of the sides
10322     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10323       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10324       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10325         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10326       }
10327       else {
10328         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10329       }
10330       break; // do not return because it's necessary to remove tmp faces
10331     }
10332
10333     // set nodes to merge
10334     // -------------------
10335
10336     if ( face[0] && face[1] )  {
10337       const int nbNodes = face[0]->NbNodes();
10338       if ( nbNodes != face[1]->NbNodes() ) {
10339         MESSAGE("Diff nb of face nodes");
10340         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10341         break; // do not return because it s necessary to remove tmp faces
10342       }
10343       bool reverse[] = { false, false }; // order of nodes in the link
10344       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10345         // analyse link orientation in faces
10346         int i1 = iLinkNode[ iSide ][ 0 ];
10347         int i2 = iLinkNode[ iSide ][ 1 ];
10348         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10349       }
10350       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10351       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10352       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10353       {
10354         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10355                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10356       }
10357
10358       // add other links of the faces to linkList
10359       // -----------------------------------------
10360
10361       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10362         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10363         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10364         if ( !iter_isnew.second ) { // already in a set: no need to process
10365           linkIdSet.erase( iter_isnew.first );
10366         }
10367         else // new in set == encountered for the first time: add
10368         {
10369           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10370           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10371           linkList[0].push_back ( NLink( n1, n2 ));
10372           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10373         }
10374       }
10375     } // 2 faces found
10376
10377     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10378       break;
10379
10380   } // loop on link lists
10381
10382   if ( aResult == SEW_OK &&
10383        ( //linkIt[0] != linkList[0].end() ||
10384          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10385     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10386              " " << (faceSetPtr[1]->empty()));
10387     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10388   }
10389
10390   // ====================================================================
10391   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10392   // ====================================================================
10393
10394   // delete temporary faces
10395 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10396 //  while ( tmpFaceIt->more() )
10397 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10398   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10399   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10400     aMesh->RemoveElement(*tmpFaceIt);
10401
10402   if ( aResult != SEW_OK)
10403     return aResult;
10404
10405   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10406   // loop on nodes replacement map
10407   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10408   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10409     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10410       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10411       nodeIDsToRemove.push_back( nToRemove->GetID() );
10412       // loop on elements sharing nToRemove
10413       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10414       while ( invElemIt->more() ) {
10415         const SMDS_MeshElement* e = invElemIt->next();
10416         // get a new suite of nodes: make replacement
10417         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10418         vector< const SMDS_MeshNode*> nodes( nbNodes );
10419         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10420         while ( nIt->more() ) {
10421           const SMDS_MeshNode* n =
10422             static_cast<const SMDS_MeshNode*>( nIt->next() );
10423           nnIt = nReplaceMap.find( n );
10424           if ( nnIt != nReplaceMap.end() ) {
10425             nbReplaced++;
10426             n = (*nnIt).second;
10427           }
10428           nodes[ i++ ] = n;
10429         }
10430         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10431         //         elemIDsToRemove.push_back( e->GetID() );
10432         //       else
10433         if ( nbReplaced )
10434           {
10435             SMDSAbs_ElementType etyp = e->GetType();
10436             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10437             if (newElem)
10438               {
10439                 myLastCreatedElems.Append(newElem);
10440                 AddToSameGroups(newElem, e, aMesh);
10441                 int aShapeId = e->getshapeId();
10442                 if ( aShapeId )
10443                   {
10444                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10445                   }
10446               }
10447             aMesh->RemoveElement(e);
10448           }
10449       }
10450     }
10451
10452   Remove( nodeIDsToRemove, true );
10453
10454   return aResult;
10455 }
10456
10457 //================================================================================
10458 /*!
10459  * \brief Find corresponding nodes in two sets of faces
10460  * \param theSide1 - first face set
10461  * \param theSide2 - second first face
10462  * \param theFirstNode1 - a boundary node of set 1
10463  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10464  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10465  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10466  * \param nReplaceMap - output map of corresponding nodes
10467  * \return bool  - is a success or not
10468  */
10469 //================================================================================
10470
10471 #ifdef _DEBUG_
10472 //#define DEBUG_MATCHING_NODES
10473 #endif
10474
10475 SMESH_MeshEditor::Sew_Error
10476 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10477                                     set<const SMDS_MeshElement*>& theSide2,
10478                                     const SMDS_MeshNode*          theFirstNode1,
10479                                     const SMDS_MeshNode*          theFirstNode2,
10480                                     const SMDS_MeshNode*          theSecondNode1,
10481                                     const SMDS_MeshNode*          theSecondNode2,
10482                                     TNodeNodeMap &                nReplaceMap)
10483 {
10484   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10485
10486   nReplaceMap.clear();
10487   if ( theFirstNode1 != theFirstNode2 )
10488     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10489   if ( theSecondNode1 != theSecondNode2 )
10490     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10491
10492   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10493   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10494
10495   list< NLink > linkList[2];
10496   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10497   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10498
10499   // loop on links in linkList; find faces by links and append links
10500   // of the found faces to linkList
10501   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10502   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10503     NLink link[] = { *linkIt[0], *linkIt[1] };
10504     if ( linkSet.find( link[0] ) == linkSet.end() )
10505       continue;
10506
10507     // by links, find faces in the face sets,
10508     // and find indices of link nodes in the found faces;
10509     // in a face set, there is only one or no face sharing a link
10510     // ---------------------------------------------------------------
10511
10512     const SMDS_MeshElement* face[] = { 0, 0 };
10513     list<const SMDS_MeshNode*> notLinkNodes[2];
10514     //bool reverse[] = { false, false }; // order of notLinkNodes
10515     int nbNodes[2];
10516     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10517     {
10518       const SMDS_MeshNode* n1 = link[iSide].first;
10519       const SMDS_MeshNode* n2 = link[iSide].second;
10520       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10521       set< const SMDS_MeshElement* > facesOfNode1;
10522       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10523       {
10524         // during a loop of the first node, we find all faces around n1,
10525         // during a loop of the second node, we find one face sharing both n1 and n2
10526         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10527         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10528         while ( fIt->more() ) { // loop on faces sharing a node
10529           const SMDS_MeshElement* f = fIt->next();
10530           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10531               ! facesOfNode1.insert( f ).second ) // f encounters twice
10532           {
10533             if ( face[ iSide ] ) {
10534               MESSAGE( "2 faces per link " );
10535               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10536             }
10537             face[ iSide ] = f;
10538             faceSet->erase( f );
10539
10540             // get not link nodes
10541             int nbN = f->NbNodes();
10542             if ( f->IsQuadratic() )
10543               nbN /= 2;
10544             nbNodes[ iSide ] = nbN;
10545             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10546             int i1 = f->GetNodeIndex( n1 );
10547             int i2 = f->GetNodeIndex( n2 );
10548             int iEnd = nbN, iBeg = -1, iDelta = 1;
10549             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10550             if ( reverse ) {
10551               std::swap( iEnd, iBeg ); iDelta = -1;
10552             }
10553             int i = i2;
10554             while ( true ) {
10555               i += iDelta;
10556               if ( i == iEnd ) i = iBeg + iDelta;
10557               if ( i == i1 ) break;
10558               nodes.push_back ( f->GetNode( i ) );
10559             }
10560           }
10561         }
10562       }
10563     }
10564     // check similarity of elements of the sides
10565     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10566       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10567       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10568         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10569       }
10570       else {
10571         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10572       }
10573     }
10574
10575     // set nodes to merge
10576     // -------------------
10577
10578     if ( face[0] && face[1] )  {
10579       if ( nbNodes[0] != nbNodes[1] ) {
10580         MESSAGE("Diff nb of face nodes");
10581         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10582       }
10583 #ifdef DEBUG_MATCHING_NODES
10584       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10585                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10586                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10587 #endif
10588       int nbN = nbNodes[0];
10589       {
10590         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10591         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10592         for ( int i = 0 ; i < nbN - 2; ++i ) {
10593 #ifdef DEBUG_MATCHING_NODES
10594           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10595 #endif
10596           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10597         }
10598       }
10599
10600       // add other links of the face 1 to linkList
10601       // -----------------------------------------
10602
10603       const SMDS_MeshElement* f0 = face[0];
10604       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10605       for ( int i = 0; i < nbN; i++ )
10606       {
10607         const SMDS_MeshNode* n2 = f0->GetNode( i );
10608         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10609           linkSet.insert( SMESH_TLink( n1, n2 ));
10610         if ( !iter_isnew.second ) { // already in a set: no need to process
10611           linkSet.erase( iter_isnew.first );
10612         }
10613         else // new in set == encountered for the first time: add
10614         {
10615 #ifdef DEBUG_MATCHING_NODES
10616           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10617                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10618 #endif
10619           linkList[0].push_back ( NLink( n1, n2 ));
10620           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10621         }
10622         n1 = n2;
10623       }
10624     } // 2 faces found
10625   } // loop on link lists
10626
10627   return SEW_OK;
10628 }
10629
10630 //================================================================================
10631 /*!
10632   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10633   \param theElems - the list of elements (edges or faces) to be replicated
10634   The nodes for duplication could be found from these elements
10635   \param theNodesNot - list of nodes to NOT replicate
10636   \param theAffectedElems - the list of elements (cells and edges) to which the 
10637   replicated nodes should be associated to.
10638   \return TRUE if operation has been completed successfully, FALSE otherwise
10639 */
10640 //================================================================================
10641
10642 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10643                                     const TIDSortedElemSet& theNodesNot,
10644                                     const TIDSortedElemSet& theAffectedElems )
10645 {
10646   myLastCreatedElems.Clear();
10647   myLastCreatedNodes.Clear();
10648
10649   if ( theElems.size() == 0 )
10650     return false;
10651
10652   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10653   if ( !aMeshDS )
10654     return false;
10655
10656   bool res = false;
10657   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10658   // duplicate elements and nodes
10659   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10660   // replce nodes by duplications
10661   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10662   return res;
10663 }
10664
10665 //================================================================================
10666 /*!
10667   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10668   \param theMeshDS - mesh instance
10669   \param theElems - the elements replicated or modified (nodes should be changed)
10670   \param theNodesNot - nodes to NOT replicate
10671   \param theNodeNodeMap - relation of old node to new created node
10672   \param theIsDoubleElem - flag os to replicate element or modify
10673   \return TRUE if operation has been completed successfully, FALSE otherwise
10674 */
10675 //================================================================================
10676
10677 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10678                                     const TIDSortedElemSet& theElems,
10679                                     const TIDSortedElemSet& theNodesNot,
10680                                     std::map< const SMDS_MeshNode*,
10681                                     const SMDS_MeshNode* >& theNodeNodeMap,
10682                                     const bool theIsDoubleElem )
10683 {
10684   MESSAGE("doubleNodes");
10685   // iterate on through element and duplicate them (by nodes duplication)
10686   bool res = false;
10687   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10688   for ( ;  elemItr != theElems.end(); ++elemItr )
10689   {
10690     const SMDS_MeshElement* anElem = *elemItr;
10691     if (!anElem)
10692       continue;
10693
10694     bool isDuplicate = false;
10695     // duplicate nodes to duplicate element
10696     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10697     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10698     int ind = 0;
10699     while ( anIter->more() ) 
10700     { 
10701
10702       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10703       SMDS_MeshNode* aNewNode = aCurrNode;
10704       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10705         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10706       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10707       {
10708         // duplicate node
10709         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10710         theNodeNodeMap[ aCurrNode ] = aNewNode;
10711         myLastCreatedNodes.Append( aNewNode );
10712       }
10713       isDuplicate |= (aCurrNode != aNewNode);
10714       newNodes[ ind++ ] = aNewNode;
10715     }
10716     if ( !isDuplicate )
10717       continue;
10718
10719     if ( theIsDoubleElem )
10720       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10721     else
10722       {
10723       MESSAGE("ChangeElementNodes");
10724       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10725       }
10726     res = true;
10727   }
10728   return res;
10729 }
10730
10731 //================================================================================
10732 /*!
10733   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10734   \param theNodes - identifiers of nodes to be doubled
10735   \param theModifiedElems - identifiers of elements to be updated by the new (doubled) 
10736          nodes. If list of element identifiers is empty then nodes are doubled but 
10737          they not assigned to elements
10738   \return TRUE if operation has been completed successfully, FALSE otherwise
10739 */
10740 //================================================================================
10741
10742 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes, 
10743                                     const std::list< int >& theListOfModifiedElems )
10744 {
10745   MESSAGE("DoubleNodes");
10746   myLastCreatedElems.Clear();
10747   myLastCreatedNodes.Clear();
10748
10749   if ( theListOfNodes.size() == 0 )
10750     return false;
10751
10752   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10753   if ( !aMeshDS )
10754     return false;
10755
10756   // iterate through nodes and duplicate them
10757
10758   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10759
10760   std::list< int >::const_iterator aNodeIter;
10761   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10762   {
10763     int aCurr = *aNodeIter;
10764     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10765     if ( !aNode )
10766       continue;
10767
10768     // duplicate node
10769
10770     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10771     if ( aNewNode )
10772     {
10773       anOldNodeToNewNode[ aNode ] = aNewNode;
10774       myLastCreatedNodes.Append( aNewNode );
10775     }
10776   }
10777
10778   // Create map of new nodes for modified elements
10779
10780   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10781
10782   std::list< int >::const_iterator anElemIter;
10783   for ( anElemIter = theListOfModifiedElems.begin(); 
10784         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10785   {
10786     int aCurr = *anElemIter;
10787     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10788     if ( !anElem )
10789       continue;
10790
10791     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10792
10793     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10794     int ind = 0;
10795     while ( anIter->more() ) 
10796     { 
10797       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10798       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10799       {
10800         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10801         aNodeArr[ ind++ ] = aNewNode;
10802       }
10803       else
10804         aNodeArr[ ind++ ] = aCurrNode;
10805     }
10806     anElemToNodes[ anElem ] = aNodeArr;
10807   }
10808
10809   // Change nodes of elements  
10810
10811   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10812     anElemToNodesIter = anElemToNodes.begin();
10813   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10814   {
10815     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10816     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10817     if ( anElem )
10818       {
10819       MESSAGE("ChangeElementNodes");
10820       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10821       }
10822   }
10823
10824   return true;
10825 }
10826
10827 namespace {
10828
10829   //================================================================================
10830   /*!
10831   \brief Check if element located inside shape
10832   \return TRUE if IN or ON shape, FALSE otherwise
10833   */
10834   //================================================================================
10835
10836   template<class Classifier>
10837   bool isInside(const SMDS_MeshElement* theElem,
10838                 Classifier&             theClassifier,
10839                 const double            theTol)
10840   {
10841     gp_XYZ centerXYZ (0, 0, 0);
10842     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10843     while (aNodeItr->more())
10844       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10845
10846     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10847     theClassifier.Perform(aPnt, theTol);
10848     TopAbs_State aState = theClassifier.State();
10849     return (aState == TopAbs_IN || aState == TopAbs_ON );
10850   }
10851
10852   //================================================================================
10853   /*!
10854    * \brief Classifier of the 3D point on the TopoDS_Face
10855    *        with interaface suitable for isInside()
10856    */
10857   //================================================================================
10858
10859   struct _FaceClassifier
10860   {
10861     Extrema_ExtPS       _extremum;
10862     BRepAdaptor_Surface _surface;
10863     TopAbs_State        _state;
10864
10865     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10866     {
10867       _extremum.Initialize( _surface,
10868                             _surface.FirstUParameter(), _surface.LastUParameter(),
10869                             _surface.FirstVParameter(), _surface.LastVParameter(),
10870                             _surface.Tolerance(), _surface.Tolerance() );
10871     }
10872     void Perform(const gp_Pnt& aPnt, double theTol)
10873     {
10874       _state = TopAbs_OUT;
10875       _extremum.Perform(aPnt);
10876       if ( _extremum.IsDone() )
10877         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10878 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10879           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10880 #else
10881           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10882 #endif
10883     }
10884     TopAbs_State State() const
10885     {
10886       return _state;
10887     }
10888   };
10889 }
10890
10891 //================================================================================
10892 /*!
10893   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10894   \param theElems - group of of elements (edges or faces) to be replicated
10895   \param theNodesNot - group of nodes not to replicate
10896   \param theShape - shape to detect affected elements (element which geometric center
10897   located on or inside shape).
10898   The replicated nodes should be associated to affected elements.
10899   \return TRUE if operation has been completed successfully, FALSE otherwise
10900 */
10901 //================================================================================
10902
10903 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10904                                             const TIDSortedElemSet& theNodesNot,
10905                                             const TopoDS_Shape&     theShape )
10906 {
10907   if ( theShape.IsNull() )
10908     return false;
10909
10910   const double aTol = Precision::Confusion();
10911   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10912   auto_ptr<_FaceClassifier>              aFaceClassifier;
10913   if ( theShape.ShapeType() == TopAbs_SOLID )
10914   {
10915     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10916     bsc3d->PerformInfinitePoint(aTol);
10917   }
10918   else if (theShape.ShapeType() == TopAbs_FACE )
10919   {
10920     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10921   }
10922
10923   // iterates on indicated elements and get elements by back references from their nodes
10924   TIDSortedElemSet anAffected;
10925   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10926   for ( ;  elemItr != theElems.end(); ++elemItr )
10927   {
10928     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10929     if (!anElem)
10930       continue;
10931
10932     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10933     while ( nodeItr->more() )
10934     {
10935       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10936       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10937         continue;
10938       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10939       while ( backElemItr->more() )
10940       {
10941         const SMDS_MeshElement* curElem = backElemItr->next();
10942         if ( curElem && theElems.find(curElem) == theElems.end() &&
10943              ( bsc3d.get() ?
10944                isInside( curElem, *bsc3d, aTol ) :
10945                isInside( curElem, *aFaceClassifier, aTol )))
10946           anAffected.insert( curElem );
10947       }
10948     }
10949   }
10950   return DoubleNodes( theElems, theNodesNot, anAffected );
10951 }
10952
10953 /*!
10954  *  \brief compute an oriented angle between two planes defined by four points.
10955  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10956  *  @param p0 base of the rotation axe
10957  *  @param p1 extremity of the rotation axe
10958  *  @param g1 belongs to the first plane
10959  *  @param g2 belongs to the second plane
10960  */
10961 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10962 {
10963 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10964 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10965 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10966 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10967   gp_Vec vref(p0, p1);
10968   gp_Vec v1(p0, g1);
10969   gp_Vec v2(p0, g2);
10970   gp_Vec n1 = vref.Crossed(v1);
10971   gp_Vec n2 = vref.Crossed(v2);
10972   return n2.AngleWithRef(n1, vref);
10973 }
10974
10975 /*!
10976  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10977  * The list of groups must describe a partition of the mesh volumes.
10978  * The nodes of the internal faces at the boundaries of the groups are doubled.
10979  * In option, the internal faces are replaced by flat elements.
10980  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10981  * The flat elements are stored in groups of volumes.
10982  * @param theElems - list of groups of volumes, where a group of volume is a set of
10983  * SMDS_MeshElements sorted by Id.
10984  * @param createJointElems - if TRUE, create the elements
10985  * @return TRUE if operation has been completed successfully, FALSE otherwise
10986  */
10987 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10988                                                      bool createJointElems)
10989 {
10990   MESSAGE("----------------------------------------------");
10991   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10992   MESSAGE("----------------------------------------------");
10993
10994   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10995   meshDS->BuildDownWardConnectivity(true);
10996   CHRONO(50);
10997   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10998
10999   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11000   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11001   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11002
11003   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11004   std::map<int,int>celldom; // cell vtkId --> domain
11005   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11006   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11007   faceDomains.clear();
11008   celldom.clear();
11009   cellDomains.clear();
11010   nodeDomains.clear();
11011   std::map<int,int> emptyMap;
11012   std::set<int> emptySet;
11013   emptyMap.clear();
11014
11015   for (int idom = 0; idom < theElems.size(); idom++)
11016     {
11017
11018       // --- build a map (face to duplicate --> volume to modify)
11019       //     with all the faces shared by 2 domains (group of elements)
11020       //     and corresponding volume of this domain, for each shared face.
11021       //     a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
11022
11023       //MESSAGE("Domain " << idom);
11024       const TIDSortedElemSet& domain = theElems[idom];
11025       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11026       for (; elemItr != domain.end(); ++elemItr)
11027         {
11028           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11029           if (!anElem)
11030             continue;
11031           int vtkId = anElem->getVtkId();
11032           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11033           int neighborsVtkIds[NBMAXNEIGHBORS];
11034           int downIds[NBMAXNEIGHBORS];
11035           unsigned char downTypes[NBMAXNEIGHBORS];
11036           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11037           for (int n = 0; n < nbNeighbors; n++)
11038             {
11039               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11040               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11041               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11042                 {
11043                   DownIdType face(downIds[n], downTypes[n]);
11044                   if (!faceDomains.count(face))
11045                     faceDomains[face] = emptyMap; // create an empty entry for face
11046                   if (!faceDomains[face].count(idom))
11047                     {
11048                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11049                       celldom[vtkId] = idom;
11050                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11051                     }
11052                 }
11053             }
11054         }
11055     }
11056
11057   //MESSAGE("Number of shared faces " << faceDomains.size());
11058   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11059
11060   // --- explore the shared faces domain by domain,
11061   //     explore the nodes of the face and see if they belong to a cell in the domain,
11062   //     which has only a node or an edge on the border (not a shared face)
11063
11064   for (int idomain = 0; idomain < theElems.size(); idomain++)
11065     {
11066       //MESSAGE("Domain " << idomain);
11067       const TIDSortedElemSet& domain = theElems[idomain];
11068       itface = faceDomains.begin();
11069       for (; itface != faceDomains.end(); ++itface)
11070         {
11071           std::map<int, int> domvol = itface->second;
11072           if (!domvol.count(idomain))
11073             continue;
11074           DownIdType face = itface->first;
11075           //MESSAGE(" --- face " << face.cellId);
11076           std::set<int> oldNodes;
11077           oldNodes.clear();
11078           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11079           std::set<int>::iterator itn = oldNodes.begin();
11080           for (; itn != oldNodes.end(); ++itn)
11081             {
11082               int oldId = *itn;
11083               //MESSAGE("     node " << oldId);
11084               std::set<int> cells;
11085               cells.clear();
11086               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11087               for (int i=0; i<l.ncells; i++)
11088                 {
11089                   int vtkId = l.cells[i];
11090                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11091                   if (!domain.count(anElem))
11092                     continue;
11093                   int vtkType = grid->GetCellType(vtkId);
11094                   int downId = grid->CellIdToDownId(vtkId);
11095                   if (downId < 0)
11096                     {
11097                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11098                       continue; // not OK at this stage of the algorithm:
11099                                 //no cells created after BuildDownWardConnectivity
11100                     }
11101                   DownIdType aCell(downId, vtkType);
11102                   if (celldom.count(vtkId))
11103                     continue;
11104                   cellDomains[aCell][idomain] = vtkId;
11105                   celldom[vtkId] = idomain;
11106                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11107                 }
11108             }
11109         }
11110     }
11111
11112   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11113   //     for each shared face, get the nodes
11114   //     for each node, for each domain of the face, create a clone of the node
11115
11116   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11117   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11118   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11119
11120   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11121   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11122   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11123
11124   for (int idomain = 0; idomain < theElems.size(); idomain++)
11125     {
11126       itface = faceDomains.begin();
11127       for (; itface != faceDomains.end(); ++itface)
11128         {
11129           std::map<int, int> domvol = itface->second;
11130           if (!domvol.count(idomain))
11131             continue;
11132           DownIdType face = itface->first;
11133           //MESSAGE(" --- face " << face.cellId);
11134           std::set<int> oldNodes;
11135           oldNodes.clear();
11136           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11137           bool isMultipleDetected = false;
11138           std::set<int>::iterator itn = oldNodes.begin();
11139           for (; itn != oldNodes.end(); ++itn)
11140             {
11141               int oldId = *itn;
11142               //MESSAGE("     node " << oldId);
11143               if (!nodeDomains.count(oldId))
11144                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11145               if (nodeDomains[oldId].empty())
11146                 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11147               std::map<int, int>::iterator itdom = domvol.begin();
11148               for (; itdom != domvol.end(); ++itdom)
11149                 {
11150                   int idom = itdom->first;
11151                   //MESSAGE("         domain " << idom);
11152                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11153                     {
11154                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11155                         {
11156                           vector<int> orderedDoms;
11157                           //MESSAGE("multiple node " << oldId);
11158                           isMultipleDetected =true;
11159                           if (mutipleNodes.count(oldId))
11160                             orderedDoms = mutipleNodes[oldId];
11161                           else
11162                             {
11163                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11164                               for (; it != nodeDomains[oldId].end(); ++it)
11165                                 orderedDoms.push_back(it->first);
11166                             }
11167                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11168                           //stringstream txt;
11169                           //for (int i=0; i<orderedDoms.size(); i++)
11170                           //  txt << orderedDoms[i] << " ";
11171                           //MESSAGE("orderedDoms " << txt.str());
11172                           mutipleNodes[oldId] = orderedDoms;
11173                         }
11174                       double *coords = grid->GetPoint(oldId);
11175                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11176                       int newId = newNode->getVtkId();
11177                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11178                       //MESSAGE("   newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
11179                     }
11180                   if (nodeDomains[oldId].size() >= 3)
11181                     {
11182                       //MESSAGE("confirm multiple node " << oldId);
11183                       isMultipleDetected =true;
11184                     }
11185                 }
11186             }
11187           if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
11188             {
11189               //MESSAGE("multiple Nodes detected on a shared face");
11190               int downId = itface->first.cellId;
11191               unsigned char cellType = itface->first.cellType;
11192               // --- shared edge or shared face ?
11193               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11194                 {
11195                   int nodes[3];
11196                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11197                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11198                     if (mutipleNodes.count(nodes[i]))
11199                       if (!mutipleNodesToFace.count(nodes[i]))
11200                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11201                }
11202               else // shared face (between two volumes)
11203                 {
11204                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11205                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11206                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11207                   for (int ie =0; ie < nbEdges; ie++)
11208                     {
11209                       int nodes[3];
11210                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11211                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11212                         {
11213                           vector<int> vn0 = mutipleNodes[nodes[0]];
11214                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11215                           sort( vn0.begin(), vn0.end() );
11216                           sort( vn1.begin(), vn1.end() );
11217                           if (vn0 == vn1)
11218                             {
11219                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11220                               double *coords = grid->GetPoint(nodes[0]);
11221                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11222                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11223                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11224                               gp_Pnt gref;
11225                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11226                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11227                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11228                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11229                               for (int id=0; id < vn0.size(); id++)
11230                                 {
11231                                   int idom = vn0[id];
11232                                   for (int ivol=0; ivol<nbvol; ivol++)
11233                                     {
11234                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11235                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11236                                       if (theElems[idom].count(elem))
11237                                         {
11238                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11239                                           domvol[idom] = svol;
11240                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11241                                           double values[3];
11242                                           vtkIdType npts = 0;
11243                                           vtkIdType* pts = 0;
11244                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11245                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11246                                           if (id ==0)
11247                                             {
11248                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11249                                               angleDom[idom] = 0;
11250                                             }
11251                                           else
11252                                             {
11253                                               gp_Pnt g(values[0], values[1], values[2]);
11254                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11255                                               //MESSAGE("  angle=" << angleDom[idom]);
11256                                             }
11257                                           break;
11258                                         }
11259                                     }
11260                                 }
11261                               map<double, int> sortedDom; // sort domains by angle
11262                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11263                                 sortedDom[ia->second] = ia->first;
11264                               vector<int> vnodes;
11265                               vector<int> vdom;
11266                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11267                                 {
11268                                   vdom.push_back(ib->second);
11269                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11270                                 }
11271                               for (int ino = 0; ino < nbNodes; ino++)
11272                                 vnodes.push_back(nodes[ino]);
11273                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11274                             }
11275                         }
11276                     }
11277                 }
11278             }
11279         }
11280     }
11281
11282   // --- iterate on shared faces (volumes to modify, face to extrude)
11283   //     get node id's of the face (id SMDS = id VTK)
11284   //     create flat element with old and new nodes if requested
11285
11286   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11287   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11288
11289   std::map<int, std::map<long,int> > nodeQuadDomains;
11290   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11291
11292   if (createJointElems)
11293     {
11294       itface = faceDomains.begin();
11295       for (; itface != faceDomains.end(); ++itface)
11296         {
11297           DownIdType face = itface->first;
11298           std::set<int> oldNodes;
11299           std::set<int>::iterator itn;
11300           oldNodes.clear();
11301           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11302
11303           std::map<int, int> domvol = itface->second;
11304           std::map<int, int>::iterator itdom = domvol.begin();
11305           int dom1 = itdom->first;
11306           int vtkVolId = itdom->second;
11307           itdom++;
11308           int dom2 = itdom->first;
11309           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11310                                                              nodeQuadDomains);
11311           stringstream grpname;
11312           grpname << "j_";
11313           if (dom1 < dom2)
11314             grpname << dom1 << "_" << dom2;
11315           else
11316             grpname << dom2 << "_" << dom1;
11317           int idg;
11318           string namegrp = grpname.str();
11319           if (!mapOfJunctionGroups.count(namegrp))
11320             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11321           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11322           if (sgrp)
11323             sgrp->Add(vol->GetID());
11324         }
11325     }
11326
11327   // --- create volumes on multiple domain intersection if requested
11328   //     iterate on mutipleNodesToFace
11329   //     iterate on edgesMultiDomains
11330
11331   if (createJointElems)
11332     {
11333       // --- iterate on mutipleNodesToFace
11334
11335       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11336       for (; itn != mutipleNodesToFace.end(); ++itn)
11337         {
11338           int node = itn->first;
11339           vector<int> orderDom = itn->second;
11340           vector<vtkIdType> orderedNodes;
11341           for (int idom = 0; idom <orderDom.size(); idom++)
11342             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11343             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11344
11345             stringstream grpname;
11346             grpname << "m2j_";
11347             grpname << 0 << "_" << 0;
11348             int idg;
11349             string namegrp = grpname.str();
11350             if (!mapOfJunctionGroups.count(namegrp))
11351               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11352             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11353             if (sgrp)
11354               sgrp->Add(face->GetID());
11355         }
11356
11357       // --- iterate on edgesMultiDomains
11358
11359       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11360       for (; ite != edgesMultiDomains.end(); ++ite)
11361         {
11362           vector<int> nodes = ite->first;
11363           vector<int> orderDom = ite->second;
11364           vector<vtkIdType> orderedNodes;
11365           if (nodes.size() == 2)
11366             {
11367               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11368               for (int ino=0; ino < nodes.size(); ino++)
11369                 if (orderDom.size() == 3)
11370                   for (int idom = 0; idom <orderDom.size(); idom++)
11371                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11372                 else
11373                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11374                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11375               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11376
11377               stringstream grpname;
11378               grpname << "mj_";
11379               grpname << 0 << "_" << 0;
11380               int idg;
11381               string namegrp = grpname.str();
11382               if (!mapOfJunctionGroups.count(namegrp))
11383                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11384               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11385               if (sgrp)
11386                 sgrp->Add(vol->GetID());
11387             }
11388           else
11389             {
11390               MESSAGE("Quadratic multiple joints not implemented");
11391               // TODO quadratic nodes
11392             }
11393         }
11394     }
11395
11396   // --- list the explicit faces and edges of the mesh that need to be modified,
11397   //     i.e. faces and edges built with one or more duplicated nodes.
11398   //     associate these faces or edges to their corresponding domain.
11399   //     only the first domain found is kept when a face or edge is shared
11400
11401   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11402   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11403   faceOrEdgeDom.clear();
11404   feDom.clear();
11405
11406   for (int idomain = 0; idomain < theElems.size(); idomain++)
11407     {
11408       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11409       for (; itnod != nodeDomains.end(); ++itnod)
11410         {
11411           int oldId = itnod->first;
11412           //MESSAGE("     node " << oldId);
11413           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11414           for (int i = 0; i < l.ncells; i++)
11415             {
11416               int vtkId = l.cells[i];
11417               int vtkType = grid->GetCellType(vtkId);
11418               int downId = grid->CellIdToDownId(vtkId);
11419               if (downId < 0)
11420                 continue; // new cells: not to be modified
11421               DownIdType aCell(downId, vtkType);
11422               int volParents[1000];
11423               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11424               for (int j = 0; j < nbvol; j++)
11425                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11426                   if (!feDom.count(vtkId))
11427                     {
11428                       feDom[vtkId] = idomain;
11429                       faceOrEdgeDom[aCell] = emptyMap;
11430                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11431                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11432                       //        << " type " << vtkType << " downId " << downId);
11433                     }
11434             }
11435         }
11436     }
11437
11438   // --- iterate on shared faces (volumes to modify, face to extrude)
11439   //     get node id's of the face
11440   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11441
11442   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11443   for (int m=0; m<3; m++)
11444     {
11445       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11446       itface = (*amap).begin();
11447       for (; itface != (*amap).end(); ++itface)
11448         {
11449           DownIdType face = itface->first;
11450           std::set<int> oldNodes;
11451           std::set<int>::iterator itn;
11452           oldNodes.clear();
11453           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11454           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11455           std::map<int, int> localClonedNodeIds;
11456
11457           std::map<int, int> domvol = itface->second;
11458           std::map<int, int>::iterator itdom = domvol.begin();
11459           for (; itdom != domvol.end(); ++itdom)
11460             {
11461               int idom = itdom->first;
11462               int vtkVolId = itdom->second;
11463               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11464               localClonedNodeIds.clear();
11465               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11466                 {
11467                   int oldId = *itn;
11468                   if (nodeDomains[oldId].count(idom))
11469                     {
11470                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11471                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11472                     }
11473                 }
11474               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11475             }
11476         }
11477     }
11478
11479   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11480   grid->BuildLinks();
11481
11482   CHRONOSTOP(50);
11483   counters::stats();
11484   return true;
11485 }
11486
11487 /*!
11488  * \brief Double nodes on some external faces and create flat elements.
11489  * Flat elements are mainly used by some types of mechanic calculations.
11490  *
11491  * Each group of the list must be constituted of faces.
11492  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11493  * @param theElems - list of groups of faces, where a group of faces is a set of
11494  * SMDS_MeshElements sorted by Id.
11495  * @return TRUE if operation has been completed successfully, FALSE otherwise
11496  */
11497 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11498 {
11499   MESSAGE("-------------------------------------------------");
11500   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11501   MESSAGE("-------------------------------------------------");
11502
11503   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11504
11505   // --- For each group of faces
11506   //     duplicate the nodes, create a flat element based on the face
11507   //     replace the nodes of the faces by their clones
11508
11509   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11510   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11511   clonedNodes.clear();
11512   intermediateNodes.clear();
11513   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11514   mapOfJunctionGroups.clear();
11515
11516   for (int idom = 0; idom < theElems.size(); idom++)
11517     {
11518       const TIDSortedElemSet& domain = theElems[idom];
11519       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11520       for (; elemItr != domain.end(); ++elemItr)
11521         {
11522           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11523           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11524           if (!aFace)
11525             continue;
11526           // MESSAGE("aFace=" << aFace->GetID());
11527           bool isQuad = aFace->IsQuadratic();
11528           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11529
11530           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11531
11532           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11533           while (nodeIt->more())
11534             {
11535               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11536               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11537               if (isMedium)
11538                 ln2.push_back(node);
11539               else
11540                 ln0.push_back(node);
11541
11542               const SMDS_MeshNode* clone = 0;
11543               if (!clonedNodes.count(node))
11544                 {
11545                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11546                   clonedNodes[node] = clone;
11547                 }
11548               else
11549                 clone = clonedNodes[node];
11550
11551               if (isMedium)
11552                 ln3.push_back(clone);
11553               else
11554                 ln1.push_back(clone);
11555
11556               const SMDS_MeshNode* inter = 0;
11557               if (isQuad && (!isMedium))
11558                 {
11559                   if (!intermediateNodes.count(node))
11560                     {
11561                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11562                       intermediateNodes[node] = inter;
11563                     }
11564                   else
11565                     inter = intermediateNodes[node];
11566                   ln4.push_back(inter);
11567                 }
11568             }
11569
11570           // --- extrude the face
11571
11572           vector<const SMDS_MeshNode*> ln;
11573           SMDS_MeshVolume* vol = 0;
11574           vtkIdType aType = aFace->GetVtkType();
11575           switch (aType)
11576           {
11577             case VTK_TRIANGLE:
11578               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11579               // MESSAGE("vol prism " << vol->GetID());
11580               ln.push_back(ln1[0]);
11581               ln.push_back(ln1[1]);
11582               ln.push_back(ln1[2]);
11583               break;
11584             case VTK_QUAD:
11585               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11586               // MESSAGE("vol hexa " << vol->GetID());
11587               ln.push_back(ln1[0]);
11588               ln.push_back(ln1[1]);
11589               ln.push_back(ln1[2]);
11590               ln.push_back(ln1[3]);
11591               break;
11592             case VTK_QUADRATIC_TRIANGLE:
11593               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11594                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11595               // MESSAGE("vol quad prism " << vol->GetID());
11596               ln.push_back(ln1[0]);
11597               ln.push_back(ln1[1]);
11598               ln.push_back(ln1[2]);
11599               ln.push_back(ln3[0]);
11600               ln.push_back(ln3[1]);
11601               ln.push_back(ln3[2]);
11602               break;
11603             case VTK_QUADRATIC_QUAD:
11604 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11605 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11606 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11607               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11608                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11609                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11610               // MESSAGE("vol quad hexa " << vol->GetID());
11611               ln.push_back(ln1[0]);
11612               ln.push_back(ln1[1]);
11613               ln.push_back(ln1[2]);
11614               ln.push_back(ln1[3]);
11615               ln.push_back(ln3[0]);
11616               ln.push_back(ln3[1]);
11617               ln.push_back(ln3[2]);
11618               ln.push_back(ln3[3]);
11619               break;
11620             case VTK_POLYGON:
11621               break;
11622             default:
11623               break;
11624           }
11625
11626           if (vol)
11627             {
11628               stringstream grpname;
11629               grpname << "jf_";
11630               grpname << idom;
11631               int idg;
11632               string namegrp = grpname.str();
11633               if (!mapOfJunctionGroups.count(namegrp))
11634                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11635               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11636               if (sgrp)
11637                 sgrp->Add(vol->GetID());
11638             }
11639
11640           // --- modify the face
11641
11642           aFace->ChangeNodes(&ln[0], ln.size());
11643         }
11644     }
11645   return true;
11646 }
11647
11648 //================================================================================
11649 /*!
11650  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11651  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11652  * \return TRUE if operation has been completed successfully, FALSE otherwise
11653  */
11654 //================================================================================
11655
11656 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11657 {
11658   // iterates on volume elements and detect all free faces on them
11659   SMESHDS_Mesh* aMesh = GetMeshDS();
11660   if (!aMesh)
11661     return false;
11662   //bool res = false;
11663   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11664   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11665   while(vIt->more())
11666   {
11667     const SMDS_MeshVolume* volume = vIt->next();
11668     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11669     vTool.SetExternalNormal();
11670     //const bool isPoly = volume->IsPoly();
11671     const int iQuad = volume->IsQuadratic();
11672     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11673     {
11674       if (!vTool.IsFreeFace(iface))
11675         continue;
11676       nbFree++;
11677       vector<const SMDS_MeshNode *> nodes;
11678       int nbFaceNodes = vTool.NbFaceNodes(iface);
11679       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11680       int inode = 0;
11681       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11682         nodes.push_back(faceNodes[inode]);
11683       if (iQuad) { // add medium nodes
11684         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11685           nodes.push_back(faceNodes[inode]);
11686         if ( nbFaceNodes == 9 ) // bi-quadratic quad
11687           nodes.push_back(faceNodes[8]);
11688       }
11689       // add new face based on volume nodes
11690       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11691         nbExisted++;
11692         continue; // face already exsist
11693       }
11694       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11695       nbCreated++;
11696     }
11697   }
11698   return ( nbFree==(nbExisted+nbCreated) );
11699 }
11700
11701 namespace
11702 {
11703   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11704   {
11705     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11706       return n;
11707     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11708   }
11709 }
11710 //================================================================================
11711 /*!
11712  * \brief Creates missing boundary elements
11713  *  \param elements - elements whose boundary is to be checked
11714  *  \param dimension - defines type of boundary elements to create
11715  *  \param group - a group to store created boundary elements in
11716  *  \param targetMesh - a mesh to store created boundary elements in
11717  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11718  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
11719  *                                boundary elements will be copied into the targetMesh
11720  *  \param toAddExistingBondary - if true, not only new but also pre-existing
11721  *                                boundary elements will be added into the new group
11722  *  \param aroundElements - if true, elements will be created on boundary of given
11723  *                          elements else, on boundary of the whole mesh.
11724  * \return nb of added boundary elements
11725  */
11726 //================================================================================
11727
11728 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11729                                        Bnd_Dimension           dimension,
11730                                        SMESH_Group*            group/*=0*/,
11731                                        SMESH_Mesh*             targetMesh/*=0*/,
11732                                        bool                    toCopyElements/*=false*/,
11733                                        bool                    toCopyExistingBoundary/*=false*/,
11734                                        bool                    toAddExistingBondary/*= false*/,
11735                                        bool                    aroundElements/*= false*/)
11736 {
11737   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11738   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11739   // hope that all elements are of the same type, do not check them all
11740   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11741     throw SALOME_Exception(LOCALIZED("wrong element type"));
11742
11743   if ( !targetMesh )
11744     toCopyElements = toCopyExistingBoundary = false;
11745
11746   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11747   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11748   int nbAddedBnd = 0;
11749
11750   // editor adding present bnd elements and optionally holding elements to add to the group
11751   SMESH_MeshEditor* presentEditor;
11752   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11753   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11754
11755   SMESH_MesherHelper helper( *myMesh );
11756   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11757   SMDS_VolumeTool vTool;
11758   TIDSortedElemSet avoidSet;
11759   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11760   int inode;
11761
11762   typedef vector<const SMDS_MeshNode*> TConnectivity;
11763
11764   SMDS_ElemIteratorPtr eIt;
11765   if (elements.empty())
11766     eIt = aMesh->elementsIterator(elemType);
11767   else
11768     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11769
11770   while (eIt->more())
11771   {
11772     const SMDS_MeshElement* elem = eIt->next();
11773     const int iQuad = elem->IsQuadratic();
11774
11775     // ------------------------------------------------------------------------------------
11776     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11777     // ------------------------------------------------------------------------------------
11778     vector<const SMDS_MeshElement*> presentBndElems;
11779     vector<TConnectivity>           missingBndElems;
11780     TConnectivity nodes;
11781     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11782     {
11783       vTool.SetExternalNormal();
11784       const SMDS_MeshElement* otherVol = 0;
11785       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11786       {
11787         if ( !vTool.IsFreeFace(iface, &otherVol) &&
11788              ( !aroundElements || elements.count( otherVol )))
11789           continue;
11790         const int nbFaceNodes = vTool.NbFaceNodes(iface);
11791         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11792         if ( missType == SMDSAbs_Edge ) // boundary edges
11793         {
11794           nodes.resize( 2+iQuad );
11795           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11796           {
11797             for ( int j = 0; j < nodes.size(); ++j )
11798               nodes[j] =nn[i+j];
11799             if ( const SMDS_MeshElement* edge =
11800                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11801               presentBndElems.push_back( edge );
11802             else
11803               missingBndElems.push_back( nodes );
11804           }
11805         }
11806         else // boundary face
11807         {
11808           nodes.clear();
11809           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11810             nodes.push_back( nn[inode] );
11811           if (iQuad) // add medium nodes
11812             for ( inode = 1; inode < nbFaceNodes; inode += 2)
11813               nodes.push_back( nn[inode] );
11814           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11815           if ( iCenter > 0 )
11816             nodes.push_back( vTool.GetNodes()[ iCenter ] );
11817
11818           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11819                                                                SMDSAbs_Face, /*noMedium=*/false ))
11820             presentBndElems.push_back( f );
11821           else
11822             missingBndElems.push_back( nodes );
11823
11824           if ( targetMesh != myMesh )
11825           {
11826             // add 1D elements on face boundary to be added to a new mesh
11827             const SMDS_MeshElement* edge;
11828             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11829             {
11830               if ( iQuad )
11831                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11832               else
11833                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11834               if ( edge && avoidSet.insert( edge ).second )
11835                 presentBndElems.push_back( edge );
11836             }
11837           }
11838         }
11839       }
11840     }
11841     else                     // elem is a face ------------------------------------------
11842     {
11843       avoidSet.clear(), avoidSet.insert( elem );
11844       int nbNodes = elem->NbCornerNodes();
11845       nodes.resize( 2 /*+ iQuad*/);
11846       for ( int i = 0; i < nbNodes; i++ )
11847       {
11848         nodes[0] = elem->GetNode(i);
11849         nodes[1] = elem->GetNode((i+1)%nbNodes);
11850         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11851           continue; // not free link
11852
11853         //if ( iQuad )
11854         //nodes[2] = elem->GetNode( i + nbNodes );
11855         if ( const SMDS_MeshElement* edge =
11856              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11857           presentBndElems.push_back( edge );
11858         else
11859           missingBndElems.push_back( nodes );
11860       }
11861     }
11862
11863     // ---------------------------------
11864     // 2. Add missing boundary elements
11865     // ---------------------------------
11866     if ( targetMesh != myMesh )
11867       // instead of making a map of nodes in this mesh and targetMesh,
11868       // we create nodes with same IDs.
11869       for ( int i = 0; i < missingBndElems.size(); ++i )
11870       {
11871         TConnectivity& srcNodes = missingBndElems[i];
11872         TConnectivity  nodes( srcNodes.size() );
11873         for ( inode = 0; inode < nodes.size(); ++inode )
11874           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11875         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11876                                                                    missType,
11877                                                                    /*noMedium=*/false))
11878           continue;
11879         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11880         ++nbAddedBnd;
11881       }
11882     else
11883       for ( int i = 0; i < missingBndElems.size(); ++i )
11884       {
11885         TConnectivity& nodes = missingBndElems[i];
11886         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11887                                                                    missType,
11888                                                                    /*noMedium=*/false))
11889           continue;
11890         SMDS_MeshElement* elem = 
11891           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11892         ++nbAddedBnd;
11893
11894         // try to set a new element to a shape
11895         if ( myMesh->HasShapeToMesh() )
11896         {
11897           bool ok = true;
11898           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11899           const int nbN = nodes.size() / (iQuad+1 );
11900           for ( inode = 0; inode < nbN && ok; ++inode )
11901           {
11902             pair<int, TopAbs_ShapeEnum> i_stype =
11903               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11904             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11905               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11906           }
11907           if ( ok && mediumShapes.size() > 1 )
11908           {
11909             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11910             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11911             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11912             {
11913               if (( ok = ( stype_i->first != stype_i_0.first )))
11914                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11915                                         aMesh->IndexToShape( stype_i_0.second ));
11916             }
11917           }
11918           if ( ok && mediumShapes.begin()->first == missShapeType )
11919             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11920         }
11921       }
11922
11923     // ----------------------------------
11924     // 3. Copy present boundary elements
11925     // ----------------------------------
11926     if ( toCopyExistingBoundary )
11927       for ( int i = 0 ; i < presentBndElems.size(); ++i )
11928       {
11929         const SMDS_MeshElement* e = presentBndElems[i];
11930         TConnectivity nodes( e->NbNodes() );
11931         for ( inode = 0; inode < nodes.size(); ++inode )
11932           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11933         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11934       }
11935     else // store present elements to add them to a group
11936       for ( int i = 0 ; i < presentBndElems.size(); ++i )
11937       {
11938         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11939       }
11940       
11941   } // loop on given elements
11942
11943   // ---------------------------------------------
11944   // 4. Fill group with boundary elements
11945   // ---------------------------------------------
11946   if ( group )
11947   {
11948     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11949       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11950         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11951   }
11952   tgtEditor.myLastCreatedElems.Clear();
11953   tgtEditor2.myLastCreatedElems.Clear();
11954
11955   // -----------------------
11956   // 5. Copy given elements
11957   // -----------------------
11958   if ( toCopyElements && targetMesh != myMesh )
11959   {
11960     if (elements.empty())
11961       eIt = aMesh->elementsIterator(elemType);
11962     else
11963       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11964     while (eIt->more())
11965     {
11966       const SMDS_MeshElement* elem = eIt->next();
11967       TConnectivity nodes( elem->NbNodes() );
11968       for ( inode = 0; inode < nodes.size(); ++inode )
11969         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11970       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11971
11972       tgtEditor.myLastCreatedElems.Clear();
11973     }
11974   }
11975   return nbAddedBnd;
11976 }