]> SALOME platform Git repositories - modules/smesh.git/blob - src/SMESH/SMESH_MeshEditor.cxx
Salome HOME
d9dc4f61f836e760b9645ae33e31eeed217e7905
[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     if ( newElemsMap.count( node ))
4035       continue; // node was extruded into edge
4036     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4037     int nbInitElems = 0;
4038     const SMDS_MeshElement* el = 0;
4039     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4040     while ( eIt->more() && nbInitElems < 2 ) {
4041       el = eIt->next();
4042       SMDSAbs_ElementType type = el->GetType();
4043       if ( type == SMDSAbs_Volume || type < highType ) continue;
4044       if ( type > highType ) {
4045         nbInitElems = 0;
4046         highType = type;
4047       }
4048       nbInitElems += elemSet.count(el);
4049     }
4050     if ( nbInitElems < 2 ) {
4051       bool NotCreateEdge = el && el->IsMediumNode(node);
4052       if(!NotCreateEdge) {
4053         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4054         list<const SMDS_MeshElement*> newEdges;
4055         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4056       }
4057     }
4058   }
4059
4060   // Make a ceiling for each element ie an equal element of last new nodes.
4061   // Find free links of faces - make edges and sweep them into faces.
4062
4063   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4064   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4065   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4066   {
4067     const SMDS_MeshElement* elem = itElem->first;
4068     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4069
4070     if(itElem->second.size()==0) continue;
4071
4072     const bool isQuadratic = elem->IsQuadratic();
4073
4074     if ( elem->GetType() == SMDSAbs_Edge ) {
4075       // create a ceiling edge
4076       if ( !isQuadratic ) {
4077         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4078                                vecNewNodes[ 1 ]->second.back())) {
4079           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4080                                                    vecNewNodes[ 1 ]->second.back()));
4081           srcElements.Append( myLastCreatedElems.Last() );
4082         }
4083       }
4084       else {
4085         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4086                                vecNewNodes[ 1 ]->second.back(),
4087                                vecNewNodes[ 2 ]->second.back())) {
4088           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4089                                                    vecNewNodes[ 1 ]->second.back(),
4090                                                    vecNewNodes[ 2 ]->second.back()));
4091           srcElements.Append( myLastCreatedElems.Last() );
4092         }
4093       }
4094     }
4095     if ( elem->GetType() != SMDSAbs_Face )
4096       continue;
4097
4098     bool hasFreeLinks = false;
4099
4100     TIDSortedElemSet avoidSet;
4101     avoidSet.insert( elem );
4102
4103     set<const SMDS_MeshNode*> aFaceLastNodes;
4104     int iNode, nbNodes = vecNewNodes.size();
4105     if ( !isQuadratic ) {
4106       // loop on the face nodes
4107       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4108         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4109         // look for free links of the face
4110         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4111         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4112         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4113         // check if a link is free
4114         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4115           hasFreeLinks = true;
4116           // make an edge and a ceiling for a new edge
4117           if ( !aMesh->FindEdge( n1, n2 )) {
4118             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4119             srcElements.Append( myLastCreatedElems.Last() );
4120           }
4121           n1 = vecNewNodes[ iNode ]->second.back();
4122           n2 = vecNewNodes[ iNext ]->second.back();
4123           if ( !aMesh->FindEdge( n1, n2 )) {
4124             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4125             srcElements.Append( myLastCreatedElems.Last() );
4126           }
4127         }
4128       }
4129     }
4130     else { // elem is quadratic face
4131       int nbn = nbNodes/2;
4132       for ( iNode = 0; iNode < nbn; iNode++ ) {
4133         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4134         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4135         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4136         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4137         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4138         // check if a link is free
4139         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4140              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4141              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4142           hasFreeLinks = true;
4143           // make an edge and a ceiling for a new edge
4144           // find medium node
4145           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4146             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4147             srcElements.Append( myLastCreatedElems.Last() );
4148           }
4149           n1 = vecNewNodes[ iNode ]->second.back();
4150           n2 = vecNewNodes[ iNext ]->second.back();
4151           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4152           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4153             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4154             srcElements.Append( myLastCreatedElems.Last() );
4155           }
4156         }
4157       }
4158       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4159         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4160       }
4161     }
4162
4163     // sweep free links into faces
4164
4165     if ( hasFreeLinks )  {
4166       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4167       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4168
4169       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4170       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4171         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4172         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4173       }
4174       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4175         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4176         std::advance( v, volNb );
4177         // find indices of free faces of a volume and their source edges
4178         list< int > freeInd;
4179         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4180         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4181         int iF, nbF = vTool.NbFaces();
4182         for ( iF = 0; iF < nbF; iF ++ ) {
4183           if (vTool.IsFreeFace( iF ) &&
4184               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4185               initNodeSet != faceNodeSet) // except an initial face
4186           {
4187             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4188               continue;
4189             freeInd.push_back( iF );
4190             // find source edge of a free face iF
4191             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4192             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4193             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4194                                    initNodeSet.begin(), initNodeSet.end(),
4195                                    commonNodes.begin());
4196             if ( (*v)->IsQuadratic() )
4197               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4198             else
4199               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4200 #ifdef _DEBUG_
4201             if ( !srcEdges.back() )
4202             {
4203               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4204                    << iF << " of volume #" << vTool.ID() << endl;
4205             }
4206 #endif
4207           }
4208         }
4209         if ( freeInd.empty() )
4210           continue;
4211
4212         // create faces for all steps;
4213         // if such a face has been already created by sweep of edge,
4214         // assure that its orientation is OK
4215         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4216           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4217           vTool.SetExternalNormal();
4218           const int nextShift = vTool.IsForward() ? +1 : -1;
4219           list< int >::iterator ind = freeInd.begin();
4220           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4221           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4222           {
4223             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4224             int nbn = vTool.NbFaceNodes( *ind );
4225             const SMDS_MeshElement * f = 0;
4226             if ( nbn == 3 )              ///// triangle
4227             {
4228               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4229               if ( !f ||
4230                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4231               {
4232                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4233                                                      nodes[ 1 ],
4234                                                      nodes[ 1 + nextShift ] };
4235                 if ( f )
4236                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4237                 else
4238                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4239                                                             newOrder[ 2 ] ));
4240               }
4241             }
4242             else if ( nbn == 4 )       ///// quadrangle
4243             {
4244               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4245               if ( !f ||
4246                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4247               {
4248                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4249                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4250                 if ( f )
4251                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4252                 else
4253                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4254                                                             newOrder[ 2 ], newOrder[ 3 ]));
4255               }
4256             }
4257             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4258             {
4259               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4260               if ( !f ||
4261                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4262               {
4263                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4264                                                      nodes[2],
4265                                                      nodes[2 + 2*nextShift],
4266                                                      nodes[3 - 2*nextShift],
4267                                                      nodes[3],
4268                                                      nodes[3 + 2*nextShift]};
4269                 if ( f )
4270                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4271                 else
4272                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4273                                                             newOrder[ 1 ],
4274                                                             newOrder[ 2 ],
4275                                                             newOrder[ 3 ],
4276                                                             newOrder[ 4 ],
4277                                                             newOrder[ 5 ] ));
4278               }
4279             }
4280             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4281             {
4282               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4283                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4284               if ( !f ||
4285                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4286               {
4287                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4288                                                      nodes[4 - 2*nextShift],
4289                                                      nodes[4],
4290                                                      nodes[4 + 2*nextShift],
4291                                                      nodes[1],
4292                                                      nodes[5 - 2*nextShift],
4293                                                      nodes[5],
4294                                                      nodes[5 + 2*nextShift] };
4295                 if ( f )
4296                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4297                 else
4298                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4299                                                            newOrder[ 2 ], newOrder[ 3 ],
4300                                                            newOrder[ 4 ], newOrder[ 5 ],
4301                                                            newOrder[ 6 ], newOrder[ 7 ]));
4302               }
4303             }
4304             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4305             {
4306               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4307                                       SMDSAbs_Face, /*noMedium=*/false);
4308               if ( !f ||
4309                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4310               {
4311                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4312                                                      nodes[4 - 2*nextShift],
4313                                                      nodes[4],
4314                                                      nodes[4 + 2*nextShift],
4315                                                      nodes[1],
4316                                                      nodes[5 - 2*nextShift],
4317                                                      nodes[5],
4318                                                      nodes[5 + 2*nextShift],
4319                                                      nodes[8] };
4320                 if ( f )
4321                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4322                 else
4323                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4324                                                            newOrder[ 2 ], newOrder[ 3 ],
4325                                                            newOrder[ 4 ], newOrder[ 5 ],
4326                                                            newOrder[ 6 ], newOrder[ 7 ],
4327                                                            newOrder[ 8 ]));
4328               }
4329             }
4330             else  //////// polygon
4331             {
4332               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4333               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4334               if ( !f ||
4335                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4336               {
4337                 if ( !vTool.IsForward() )
4338                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4339                 if ( f )
4340                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4341                 else
4342                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4343               }
4344             }
4345
4346             while ( srcElements.Length() < myLastCreatedElems.Length() )
4347               srcElements.Append( *srcEdge );
4348
4349           }  // loop on free faces
4350
4351           // go to the next volume
4352           iVol = 0;
4353           while ( iVol++ < nbVolumesByStep ) v++;
4354
4355         } // loop on steps
4356       } // loop on volumes of one step
4357     } // sweep free links into faces
4358
4359     // Make a ceiling face with a normal external to a volume
4360
4361     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4362
4363     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4364     if ( iF >= 0 ) {
4365       lastVol.SetExternalNormal();
4366       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4367       int nbn = lastVol.NbFaceNodes( iF );
4368       if ( nbn == 3 ) {
4369         if (!hasFreeLinks ||
4370             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4371           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4372       }
4373       else if ( nbn == 4 )
4374       {
4375         if (!hasFreeLinks ||
4376             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4377           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4378       }
4379       else if ( nbn == 6 && isQuadratic )
4380       {
4381         if (!hasFreeLinks ||
4382             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4383           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4384                                                    nodes[1], nodes[3], nodes[5]));
4385       }
4386       else if ( nbn == 8 && isQuadratic )
4387       {
4388         if (!hasFreeLinks ||
4389             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4390                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4391           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4392                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4393       }
4394       else if ( nbn == 9 && isQuadratic )
4395       {
4396         if (!hasFreeLinks ||
4397             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4398                                 SMDSAbs_Face, /*noMedium=*/false) )
4399           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4400                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4401                                                    nodes[8]));
4402       }
4403       else {
4404         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4405         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4406           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4407       }
4408
4409       while ( srcElements.Length() < myLastCreatedElems.Length() )
4410         srcElements.Append( myLastCreatedElems.Last() );
4411     }
4412   } // loop on swept elements
4413 }
4414
4415 //=======================================================================
4416 //function : RotationSweep
4417 //purpose  :
4418 //=======================================================================
4419
4420 SMESH_MeshEditor::PGroupIDs
4421 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4422                                 const gp_Ax1&      theAxis,
4423                                 const double       theAngle,
4424                                 const int          theNbSteps,
4425                                 const double       theTol,
4426                                 const bool         theMakeGroups,
4427                                 const bool         theMakeWalls)
4428 {
4429   myLastCreatedElems.Clear();
4430   myLastCreatedNodes.Clear();
4431
4432   // source elements for each generated one
4433   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4434
4435   MESSAGE( "RotationSweep()");
4436   gp_Trsf aTrsf;
4437   aTrsf.SetRotation( theAxis, theAngle );
4438   gp_Trsf aTrsf2;
4439   aTrsf2.SetRotation( theAxis, theAngle/2. );
4440
4441   gp_Lin aLine( theAxis );
4442   double aSqTol = theTol * theTol;
4443
4444   SMESHDS_Mesh* aMesh = GetMeshDS();
4445
4446   TNodeOfNodeListMap mapNewNodes;
4447   TElemOfVecOfNnlmiMap mapElemNewNodes;
4448   TElemOfElemListMap newElemsMap;
4449
4450   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4451                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4452                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4453   // loop on theElems
4454   TIDSortedElemSet::iterator itElem;
4455   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4456     const SMDS_MeshElement* elem = *itElem;
4457     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4458       continue;
4459     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4460     newNodesItVec.reserve( elem->NbNodes() );
4461
4462     // loop on elem nodes
4463     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4464     while ( itN->more() )
4465     {
4466       // check if a node has been already sweeped
4467       const SMDS_MeshNode* node = cast2Node( itN->next() );
4468
4469       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4470       double coord[3];
4471       aXYZ.Coord( coord[0], coord[1], coord[2] );
4472       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4473
4474       TNodeOfNodeListMapItr nIt =
4475         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4476       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4477       if ( listNewNodes.empty() )
4478       {
4479         // check if we are to create medium nodes between corner ones
4480         bool needMediumNodes = false;
4481         if ( isQuadraticMesh )
4482         {
4483           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4484           while (it->more() && !needMediumNodes )
4485           {
4486             const SMDS_MeshElement* invElem = it->next();
4487             if ( invElem != elem && !theElems.count( invElem )) continue;
4488             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4489             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4490               needMediumNodes = true;
4491           }
4492         }
4493
4494         // make new nodes
4495         const SMDS_MeshNode * newNode = node;
4496         for ( int i = 0; i < theNbSteps; i++ ) {
4497           if ( !isOnAxis ) {
4498             if ( needMediumNodes )  // create a medium node
4499             {
4500               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4501               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4502               myLastCreatedNodes.Append(newNode);
4503               srcNodes.Append( node );
4504               listNewNodes.push_back( newNode );
4505               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4506             }
4507             else {
4508               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4509             }
4510             // create a corner node
4511             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4512             myLastCreatedNodes.Append(newNode);
4513             srcNodes.Append( node );
4514             listNewNodes.push_back( newNode );
4515           }
4516           else {
4517             listNewNodes.push_back( newNode );
4518             // if ( needMediumNodes )
4519             //   listNewNodes.push_back( newNode );
4520           }
4521         }
4522       }
4523       newNodesItVec.push_back( nIt );
4524     }
4525     // make new elements
4526     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4527   }
4528
4529   if ( theMakeWalls )
4530     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4531
4532   PGroupIDs newGroupIDs;
4533   if ( theMakeGroups )
4534     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4535
4536   return newGroupIDs;
4537 }
4538
4539
4540 //=======================================================================
4541 //function : CreateNode
4542 //purpose  :
4543 //=======================================================================
4544 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4545                                                   const double y,
4546                                                   const double z,
4547                                                   const double tolnode,
4548                                                   SMESH_SequenceOfNode& aNodes)
4549 {
4550   // myLastCreatedElems.Clear();
4551   // myLastCreatedNodes.Clear();
4552
4553   gp_Pnt P1(x,y,z);
4554   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4555
4556   // try to search in sequence of existing nodes
4557   // if aNodes.Length()>0 we 'nave to use given sequence
4558   // else - use all nodes of mesh
4559   if(aNodes.Length()>0) {
4560     int i;
4561     for(i=1; i<=aNodes.Length(); i++) {
4562       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4563       if(P1.Distance(P2)<tolnode)
4564         return aNodes.Value(i);
4565     }
4566   }
4567   else {
4568     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4569     while(itn->more()) {
4570       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4571       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4572       if(P1.Distance(P2)<tolnode)
4573         return aN;
4574     }
4575   }
4576
4577   // create new node and return it
4578   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4579   //myLastCreatedNodes.Append(NewNode);
4580   return NewNode;
4581 }
4582
4583
4584 //=======================================================================
4585 //function : ExtrusionSweep
4586 //purpose  :
4587 //=======================================================================
4588
4589 SMESH_MeshEditor::PGroupIDs
4590 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4591                                   const gp_Vec&       theStep,
4592                                   const int           theNbSteps,
4593                                   TElemOfElemListMap& newElemsMap,
4594                                   const bool          theMakeGroups,
4595                                   const int           theFlags,
4596                                   const double        theTolerance)
4597 {
4598   ExtrusParam aParams;
4599   aParams.myDir = gp_Dir(theStep);
4600   aParams.myNodes.Clear();
4601   aParams.mySteps = new TColStd_HSequenceOfReal;
4602   int i;
4603   for(i=1; i<=theNbSteps; i++)
4604     aParams.mySteps->Append(theStep.Magnitude());
4605
4606   return
4607     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4608 }
4609
4610
4611 //=======================================================================
4612 //function : ExtrusionSweep
4613 //purpose  :
4614 //=======================================================================
4615
4616 SMESH_MeshEditor::PGroupIDs
4617 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4618                                   ExtrusParam&        theParams,
4619                                   TElemOfElemListMap& newElemsMap,
4620                                   const bool          theMakeGroups,
4621                                   const int           theFlags,
4622                                   const double        theTolerance)
4623 {
4624   myLastCreatedElems.Clear();
4625   myLastCreatedNodes.Clear();
4626
4627   // source elements for each generated one
4628   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4629
4630   SMESHDS_Mesh* aMesh = GetMeshDS();
4631
4632   int nbsteps = theParams.mySteps->Length();
4633
4634   TNodeOfNodeListMap mapNewNodes;
4635   //TNodeOfNodeVecMap mapNewNodes;
4636   TElemOfVecOfNnlmiMap mapElemNewNodes;
4637   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4638
4639   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4640                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4641                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4642   // loop on theElems
4643   TIDSortedElemSet::iterator itElem;
4644   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4645     // check element type
4646     const SMDS_MeshElement* elem = *itElem;
4647     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4648       continue;
4649
4650     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4651     newNodesItVec.reserve( elem->NbNodes() );
4652
4653     // loop on elem nodes
4654     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4655     while ( itN->more() )
4656     {
4657       // check if a node has been already sweeped
4658       const SMDS_MeshNode* node = cast2Node( itN->next() );
4659       TNodeOfNodeListMap::iterator nIt =
4660         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4661       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4662       if ( listNewNodes.empty() )
4663       {
4664         // make new nodes
4665
4666         // check if we are to create medium nodes between corner ones
4667         bool needMediumNodes = false;
4668         if ( isQuadraticMesh )
4669         {
4670           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4671           while (it->more() && !needMediumNodes )
4672           {
4673             const SMDS_MeshElement* invElem = it->next();
4674             if ( invElem != elem && !theElems.count( invElem )) continue;
4675             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4676             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4677               needMediumNodes = true;
4678           }
4679         }
4680
4681         double coord[] = { node->X(), node->Y(), node->Z() };
4682         for ( int i = 0; i < nbsteps; i++ )
4683         {
4684           if ( needMediumNodes ) // create a medium node
4685           {
4686             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4687             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4688             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4689             if( theFlags & EXTRUSION_FLAG_SEW ) {
4690               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4691                                                          theTolerance, theParams.myNodes);
4692               listNewNodes.push_back( newNode );
4693             }
4694             else {
4695               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4696               myLastCreatedNodes.Append(newNode);
4697               srcNodes.Append( node );
4698               listNewNodes.push_back( newNode );
4699             }
4700           }
4701           // create a corner node
4702           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4703           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4704           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4705           if( theFlags & EXTRUSION_FLAG_SEW ) {
4706             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4707                                                        theTolerance, theParams.myNodes);
4708             listNewNodes.push_back( newNode );
4709           }
4710           else {
4711             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4712             myLastCreatedNodes.Append(newNode);
4713             srcNodes.Append( node );
4714             listNewNodes.push_back( newNode );
4715           }
4716         }
4717       }
4718       newNodesItVec.push_back( nIt );
4719     }
4720     // make new elements
4721     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4722   }
4723
4724   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4725     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4726   }
4727   PGroupIDs newGroupIDs;
4728   if ( theMakeGroups )
4729     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4730
4731   return newGroupIDs;
4732 }
4733
4734 //=======================================================================
4735 //function : ExtrusionAlongTrack
4736 //purpose  :
4737 //=======================================================================
4738 SMESH_MeshEditor::Extrusion_Error
4739 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4740                                        SMESH_subMesh*       theTrack,
4741                                        const SMDS_MeshNode* theN1,
4742                                        const bool           theHasAngles,
4743                                        list<double>&        theAngles,
4744                                        const bool           theLinearVariation,
4745                                        const bool           theHasRefPoint,
4746                                        const gp_Pnt&        theRefPoint,
4747                                        const bool           theMakeGroups)
4748 {
4749   MESSAGE("ExtrusionAlongTrack");
4750   myLastCreatedElems.Clear();
4751   myLastCreatedNodes.Clear();
4752
4753   int aNbE;
4754   std::list<double> aPrms;
4755   TIDSortedElemSet::iterator itElem;
4756
4757   gp_XYZ aGC;
4758   TopoDS_Edge aTrackEdge;
4759   TopoDS_Vertex aV1, aV2;
4760
4761   SMDS_ElemIteratorPtr aItE;
4762   SMDS_NodeIteratorPtr aItN;
4763   SMDSAbs_ElementType aTypeE;
4764
4765   TNodeOfNodeListMap mapNewNodes;
4766
4767   // 1. Check data
4768   aNbE = theElements.size();
4769   // nothing to do
4770   if ( !aNbE )
4771     return EXTR_NO_ELEMENTS;
4772
4773   // 1.1 Track Pattern
4774   ASSERT( theTrack );
4775
4776   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4777
4778   aItE = pSubMeshDS->GetElements();
4779   while ( aItE->more() ) {
4780     const SMDS_MeshElement* pE = aItE->next();
4781     aTypeE = pE->GetType();
4782     // Pattern must contain links only
4783     if ( aTypeE != SMDSAbs_Edge )
4784       return EXTR_PATH_NOT_EDGE;
4785   }
4786
4787   list<SMESH_MeshEditor_PathPoint> fullList;
4788
4789   const TopoDS_Shape& aS = theTrack->GetSubShape();
4790   // Sub-shape for the Pattern must be an Edge or Wire
4791   if( aS.ShapeType() == TopAbs_EDGE ) {
4792     aTrackEdge = TopoDS::Edge( aS );
4793     // the Edge must not be degenerated
4794     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4795       return EXTR_BAD_PATH_SHAPE;
4796     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4797     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4798     const SMDS_MeshNode* aN1 = aItN->next();
4799     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4800     const SMDS_MeshNode* aN2 = aItN->next();
4801     // starting node must be aN1 or aN2
4802     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4803       return EXTR_BAD_STARTING_NODE;
4804     aItN = pSubMeshDS->GetNodes();
4805     while ( aItN->more() ) {
4806       const SMDS_MeshNode* pNode = aItN->next();
4807       const SMDS_EdgePosition* pEPos =
4808         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4809       double aT = pEPos->GetUParameter();
4810       aPrms.push_back( aT );
4811     }
4812     //Extrusion_Error err =
4813     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4814   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4815     list< SMESH_subMesh* > LSM;
4816     TopTools_SequenceOfShape Edges;
4817     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4818     while(itSM->more()) {
4819       SMESH_subMesh* SM = itSM->next();
4820       LSM.push_back(SM);
4821       const TopoDS_Shape& aS = SM->GetSubShape();
4822       Edges.Append(aS);
4823     }
4824     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4825     int startNid = theN1->GetID();
4826     TColStd_MapOfInteger UsedNums;
4827     
4828     int NbEdges = Edges.Length();
4829     int i = 1;
4830     for(; i<=NbEdges; i++) {
4831       int k = 0;
4832       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4833       for(; itLSM!=LSM.end(); itLSM++) {
4834         k++;
4835         if(UsedNums.Contains(k)) continue;
4836         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4837         SMESH_subMesh* locTrack = *itLSM;
4838         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4839         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4840         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4841         const SMDS_MeshNode* aN1 = aItN->next();
4842         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4843         const SMDS_MeshNode* aN2 = aItN->next();
4844         // starting node must be aN1 or aN2
4845         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4846         // 2. Collect parameters on the track edge
4847         aPrms.clear();
4848         aItN = locMeshDS->GetNodes();
4849         while ( aItN->more() ) {
4850           const SMDS_MeshNode* pNode = aItN->next();
4851           const SMDS_EdgePosition* pEPos =
4852             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4853           double aT = pEPos->GetUParameter();
4854           aPrms.push_back( aT );
4855         }
4856         list<SMESH_MeshEditor_PathPoint> LPP;
4857         //Extrusion_Error err =
4858         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4859         LLPPs.push_back(LPP);
4860         UsedNums.Add(k);
4861         // update startN for search following egde
4862         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4863         else startNid = aN1->GetID();
4864         break;
4865       }
4866     }
4867     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4868     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4869     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4870     for(; itPP!=firstList.end(); itPP++) {
4871       fullList.push_back( *itPP );
4872     }
4873     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4874     fullList.pop_back();
4875     itLLPP++;
4876     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4877       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4878       itPP = currList.begin();
4879       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4880       gp_Dir D1 = PP1.Tangent();
4881       gp_Dir D2 = PP2.Tangent();
4882       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4883                            (D1.Z()+D2.Z())/2 ) );
4884       PP1.SetTangent(Dnew);
4885       fullList.push_back(PP1);
4886       itPP++;
4887       for(; itPP!=firstList.end(); itPP++) {
4888         fullList.push_back( *itPP );
4889       }
4890       PP1 = fullList.back();
4891       fullList.pop_back();
4892     }
4893     // if wire not closed
4894     fullList.push_back(PP1);
4895     // else ???
4896   }
4897   else {
4898     return EXTR_BAD_PATH_SHAPE;
4899   }
4900
4901   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4902                           theHasRefPoint, theRefPoint, theMakeGroups);
4903 }
4904
4905
4906 //=======================================================================
4907 //function : ExtrusionAlongTrack
4908 //purpose  :
4909 //=======================================================================
4910 SMESH_MeshEditor::Extrusion_Error
4911 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4912                                        SMESH_Mesh*          theTrack,
4913                                        const SMDS_MeshNode* theN1,
4914                                        const bool           theHasAngles,
4915                                        list<double>&        theAngles,
4916                                        const bool           theLinearVariation,
4917                                        const bool           theHasRefPoint,
4918                                        const gp_Pnt&        theRefPoint,
4919                                        const bool           theMakeGroups)
4920 {
4921   myLastCreatedElems.Clear();
4922   myLastCreatedNodes.Clear();
4923
4924   int aNbE;
4925   std::list<double> aPrms;
4926   TIDSortedElemSet::iterator itElem;
4927
4928   gp_XYZ aGC;
4929   TopoDS_Edge aTrackEdge;
4930   TopoDS_Vertex aV1, aV2;
4931
4932   SMDS_ElemIteratorPtr aItE;
4933   SMDS_NodeIteratorPtr aItN;
4934   SMDSAbs_ElementType aTypeE;
4935
4936   TNodeOfNodeListMap mapNewNodes;
4937
4938   // 1. Check data
4939   aNbE = theElements.size();
4940   // nothing to do
4941   if ( !aNbE )
4942     return EXTR_NO_ELEMENTS;
4943
4944   // 1.1 Track Pattern
4945   ASSERT( theTrack );
4946
4947   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4948
4949   aItE = pMeshDS->elementsIterator();
4950   while ( aItE->more() ) {
4951     const SMDS_MeshElement* pE = aItE->next();
4952     aTypeE = pE->GetType();
4953     // Pattern must contain links only
4954     if ( aTypeE != SMDSAbs_Edge )
4955       return EXTR_PATH_NOT_EDGE;
4956   }
4957
4958   list<SMESH_MeshEditor_PathPoint> fullList;
4959
4960   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4961
4962   if( aS == SMESH_Mesh::PseudoShape() ) {
4963     //Mesh without shape
4964     const SMDS_MeshNode* currentNode = NULL;
4965     const SMDS_MeshNode* prevNode = theN1;
4966     std::vector<const SMDS_MeshNode*> aNodesList;
4967     aNodesList.push_back(theN1);
4968     int nbEdges = 0, conn=0;
4969     const SMDS_MeshElement* prevElem = NULL;
4970     const SMDS_MeshElement* currentElem = NULL;
4971     int totalNbEdges = theTrack->NbEdges();
4972     SMDS_ElemIteratorPtr nIt;
4973
4974     //check start node
4975     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
4976       return EXTR_BAD_STARTING_NODE;
4977     }
4978     
4979     conn = nbEdgeConnectivity(theN1);
4980     if(conn > 2)
4981       return EXTR_PATH_NOT_EDGE;
4982
4983     aItE = theN1->GetInverseElementIterator();
4984     prevElem = aItE->next();
4985     currentElem = prevElem;
4986     //Get all nodes
4987     if(totalNbEdges == 1 ) {
4988       nIt = currentElem->nodesIterator();
4989       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4990       if(currentNode == prevNode)
4991         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4992       aNodesList.push_back(currentNode);
4993     } else { 
4994       nIt = currentElem->nodesIterator();
4995       while( nIt->more() ) {
4996         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4997         if(currentNode == prevNode)
4998           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4999         aNodesList.push_back(currentNode);
5000         
5001         //case of the closed mesh
5002         if(currentNode == theN1) {
5003           nbEdges++;
5004           break;
5005         }
5006
5007         conn = nbEdgeConnectivity(currentNode);
5008         if(conn > 2) {
5009           return EXTR_PATH_NOT_EDGE;    
5010         }else if( conn == 1 && nbEdges > 0 ) {
5011           //End of the path
5012           nbEdges++;
5013           break;
5014         }else {
5015           prevNode = currentNode;
5016           aItE = currentNode->GetInverseElementIterator();
5017           currentElem = aItE->next();
5018           if( currentElem  == prevElem)
5019             currentElem = aItE->next();
5020           nIt = currentElem->nodesIterator();
5021           prevElem = currentElem;
5022           nbEdges++;
5023         }
5024       }
5025     } 
5026     
5027     if(nbEdges != totalNbEdges)
5028       return EXTR_PATH_NOT_EDGE;
5029
5030     TopTools_SequenceOfShape Edges;
5031     double x1,x2,y1,y2,z1,z2;
5032     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5033     int startNid = theN1->GetID();
5034     for(int i = 1; i < aNodesList.size(); i++) {
5035       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5036       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5037       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5038       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));  
5039       list<SMESH_MeshEditor_PathPoint> LPP;
5040       aPrms.clear();
5041       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5042       LLPPs.push_back(LPP);
5043       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5044       else startNid = aNodesList[i-1]->GetID();
5045
5046     }
5047
5048     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5049     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5050     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5051     for(; itPP!=firstList.end(); itPP++) {
5052       fullList.push_back( *itPP );
5053     }
5054
5055     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5056     SMESH_MeshEditor_PathPoint PP2;
5057     fullList.pop_back();
5058     itLLPP++;
5059     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5060       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5061       itPP = currList.begin();
5062       PP2 = currList.front();
5063       gp_Dir D1 = PP1.Tangent();
5064       gp_Dir D2 = PP2.Tangent();
5065       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5066                            (D1.Z()+D2.Z())/2 ) );
5067       PP1.SetTangent(Dnew);
5068       fullList.push_back(PP1);
5069       itPP++;
5070       for(; itPP!=currList.end(); itPP++) {
5071         fullList.push_back( *itPP );
5072       }
5073       PP1 = fullList.back();
5074       fullList.pop_back();
5075     }
5076     fullList.push_back(PP1);
5077     
5078   } // Sub-shape for the Pattern must be an Edge or Wire
5079   else if( aS.ShapeType() == TopAbs_EDGE ) {
5080     aTrackEdge = TopoDS::Edge( aS );
5081     // the Edge must not be degenerated
5082     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5083       return EXTR_BAD_PATH_SHAPE;
5084     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5085     aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5086     const SMDS_MeshNode* aN1 = aItN->next();
5087     aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5088     const SMDS_MeshNode* aN2 = aItN->next();
5089     // starting node must be aN1 or aN2
5090     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5091       return EXTR_BAD_STARTING_NODE;
5092     aItN = pMeshDS->nodesIterator();
5093     while ( aItN->more() ) {
5094       const SMDS_MeshNode* pNode = aItN->next();
5095       if( pNode==aN1 || pNode==aN2 ) continue;
5096       const SMDS_EdgePosition* pEPos =
5097         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5098       double aT = pEPos->GetUParameter();
5099       aPrms.push_back( aT );
5100     }
5101     //Extrusion_Error err =
5102     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5103   }
5104   else if( aS.ShapeType() == TopAbs_WIRE ) {
5105     list< SMESH_subMesh* > LSM;
5106     TopTools_SequenceOfShape Edges;
5107     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5108     for(; eExp.More(); eExp.Next()) {
5109       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5110       if( BRep_Tool::Degenerated(E) ) continue;
5111       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5112       if(SM) {
5113         LSM.push_back(SM);
5114         Edges.Append(E);
5115       }
5116     }
5117     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5118     int startNid = theN1->GetID();
5119     TColStd_MapOfInteger UsedNums;
5120     int NbEdges = Edges.Length();
5121     int i = 1;
5122     for(; i<=NbEdges; i++) {
5123       int k = 0;
5124       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5125       for(; itLSM!=LSM.end(); itLSM++) {
5126         k++;
5127         if(UsedNums.Contains(k)) continue;
5128         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5129         SMESH_subMesh* locTrack = *itLSM;
5130         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5131         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5132         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5133         const SMDS_MeshNode* aN1 = aItN->next();
5134         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5135         const SMDS_MeshNode* aN2 = aItN->next();
5136         // starting node must be aN1 or aN2
5137         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5138         // 2. Collect parameters on the track edge
5139         aPrms.clear();
5140         aItN = locMeshDS->GetNodes();
5141         while ( aItN->more() ) {
5142           const SMDS_MeshNode* pNode = aItN->next();
5143           const SMDS_EdgePosition* pEPos =
5144             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5145           double aT = pEPos->GetUParameter();
5146           aPrms.push_back( aT );
5147         }
5148         list<SMESH_MeshEditor_PathPoint> LPP;
5149         //Extrusion_Error err =
5150         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5151         LLPPs.push_back(LPP);
5152         UsedNums.Add(k);
5153         // update startN for search following egde
5154         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5155         else startNid = aN1->GetID();
5156         break;
5157       }
5158     }
5159     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5160     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5161     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5162     for(; itPP!=firstList.end(); itPP++) {
5163       fullList.push_back( *itPP );
5164     }
5165     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5166     fullList.pop_back();
5167     itLLPP++;
5168     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5169       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5170       itPP = currList.begin();
5171       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5172       gp_Dir D1 = PP1.Tangent();
5173       gp_Dir D2 = PP2.Tangent();
5174       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5175                            (D1.Z()+D2.Z())/2 ) );
5176       PP1.SetTangent(Dnew);
5177       fullList.push_back(PP1);
5178       itPP++;
5179       for(; itPP!=currList.end(); itPP++) {
5180         fullList.push_back( *itPP );
5181       }
5182       PP1 = fullList.back();
5183       fullList.pop_back();
5184     }
5185     // if wire not closed
5186     fullList.push_back(PP1);
5187     // else ???
5188   }
5189   else {
5190     return EXTR_BAD_PATH_SHAPE;
5191   }
5192
5193   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5194                           theHasRefPoint, theRefPoint, theMakeGroups);
5195 }
5196
5197
5198 //=======================================================================
5199 //function : MakeEdgePathPoints
5200 //purpose  : auxilary for ExtrusionAlongTrack
5201 //=======================================================================
5202 SMESH_MeshEditor::Extrusion_Error
5203 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5204                                      const TopoDS_Edge& aTrackEdge,
5205                                      bool FirstIsStart,
5206                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5207 {
5208   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5209   aTolVec=1.e-7;
5210   aTolVec2=aTolVec*aTolVec;
5211   double aT1, aT2;
5212   TopoDS_Vertex aV1, aV2;
5213   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5214   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5215   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5216   // 2. Collect parameters on the track edge
5217   aPrms.push_front( aT1 );
5218   aPrms.push_back( aT2 );
5219   // sort parameters
5220   aPrms.sort();
5221   if( FirstIsStart ) {
5222     if ( aT1 > aT2 ) {
5223       aPrms.reverse();
5224     }
5225   }
5226   else {
5227     if ( aT2 > aT1 ) {
5228       aPrms.reverse();
5229     }
5230   }
5231   // 3. Path Points
5232   SMESH_MeshEditor_PathPoint aPP;
5233   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5234   std::list<double>::iterator aItD = aPrms.begin();
5235   for(; aItD != aPrms.end(); ++aItD) {
5236     double aT = *aItD;
5237     gp_Pnt aP3D;
5238     gp_Vec aVec;
5239     aC3D->D1( aT, aP3D, aVec );
5240     aL2 = aVec.SquareMagnitude();
5241     if ( aL2 < aTolVec2 )
5242       return EXTR_CANT_GET_TANGENT;
5243     gp_Dir aTgt( aVec );
5244     aPP.SetPnt( aP3D );
5245     aPP.SetTangent( aTgt );
5246     aPP.SetParameter( aT );
5247     LPP.push_back(aPP);
5248   }
5249   return EXTR_OK;
5250 }
5251
5252
5253 //=======================================================================
5254 //function : MakeExtrElements
5255 //purpose  : auxilary for ExtrusionAlongTrack
5256 //=======================================================================
5257 SMESH_MeshEditor::Extrusion_Error
5258 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5259                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5260                                    const bool theHasAngles,
5261                                    list<double>& theAngles,
5262                                    const bool theLinearVariation,
5263                                    const bool theHasRefPoint,
5264                                    const gp_Pnt& theRefPoint,
5265                                    const bool theMakeGroups)
5266 {
5267   MESSAGE("MakeExtrElements");
5268   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5269   int aNbTP = fullList.size();
5270   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5271   // Angles
5272   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5273     LinearAngleVariation(aNbTP-1, theAngles);
5274   }
5275   vector<double> aAngles( aNbTP );
5276   int j = 0;
5277   for(; j<aNbTP; ++j) {
5278     aAngles[j] = 0.;
5279   }
5280   if ( theHasAngles ) {
5281     double anAngle;;
5282     std::list<double>::iterator aItD = theAngles.begin();
5283     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5284       anAngle = *aItD;
5285       aAngles[j] = anAngle;
5286     }
5287   }
5288   // fill vector of path points with angles
5289   //aPPs.resize(fullList.size());
5290   j = -1;
5291   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5292   for(; itPP!=fullList.end(); itPP++) {
5293     j++;
5294     SMESH_MeshEditor_PathPoint PP = *itPP;
5295     PP.SetAngle(aAngles[j]);
5296     aPPs[j] = PP;
5297   }
5298
5299   TNodeOfNodeListMap mapNewNodes;
5300   TElemOfVecOfNnlmiMap mapElemNewNodes;
5301   TElemOfElemListMap newElemsMap;
5302   TIDSortedElemSet::iterator itElem;
5303   double aX, aY, aZ;
5304   int aNb;
5305   SMDSAbs_ElementType aTypeE;
5306   // source elements for each generated one
5307   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5308
5309   // 3. Center of rotation aV0
5310   gp_Pnt aV0 = theRefPoint;
5311   gp_XYZ aGC;
5312   if ( !theHasRefPoint ) {
5313     aNb = 0;
5314     aGC.SetCoord( 0.,0.,0. );
5315
5316     itElem = theElements.begin();
5317     for ( ; itElem != theElements.end(); itElem++ ) {
5318       const SMDS_MeshElement* elem = *itElem;
5319
5320       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5321       while ( itN->more() ) {
5322         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5323         aX = node->X();
5324         aY = node->Y();
5325         aZ = node->Z();
5326
5327         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5328           list<const SMDS_MeshNode*> aLNx;
5329           mapNewNodes[node] = aLNx;
5330           //
5331           gp_XYZ aXYZ( aX, aY, aZ );
5332           aGC += aXYZ;
5333           ++aNb;
5334         }
5335       }
5336     }
5337     aGC /= aNb;
5338     aV0.SetXYZ( aGC );
5339   } // if (!theHasRefPoint) {
5340   mapNewNodes.clear();
5341
5342   // 4. Processing the elements
5343   SMESHDS_Mesh* aMesh = GetMeshDS();
5344
5345   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5346     // check element type
5347     const SMDS_MeshElement* elem = *itElem;
5348     aTypeE = elem->GetType();
5349     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5350       continue;
5351
5352     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5353     newNodesItVec.reserve( elem->NbNodes() );
5354
5355     // loop on elem nodes
5356     int nodeIndex = -1;
5357     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5358     while ( itN->more() )
5359     {
5360       ++nodeIndex;
5361       // check if a node has been already processed
5362       const SMDS_MeshNode* node =
5363         static_cast<const SMDS_MeshNode*>( itN->next() );
5364       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5365       if ( nIt == mapNewNodes.end() ) {
5366         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5367         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5368
5369         // make new nodes
5370         aX = node->X();  aY = node->Y(); aZ = node->Z();
5371
5372         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5373         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5374         gp_Ax1 anAx1, anAxT1T0;
5375         gp_Dir aDT1x, aDT0x, aDT1T0;
5376
5377         aTolAng=1.e-4;
5378
5379         aV0x = aV0;
5380         aPN0.SetCoord(aX, aY, aZ);
5381
5382         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5383         aP0x = aPP0.Pnt();
5384         aDT0x= aPP0.Tangent();
5385         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5386
5387         for ( j = 1; j < aNbTP; ++j ) {
5388           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5389           aP1x = aPP1.Pnt();
5390           aDT1x = aPP1.Tangent();
5391           aAngle1x = aPP1.Angle();
5392
5393           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5394           // Translation
5395           gp_Vec aV01x( aP0x, aP1x );
5396           aTrsf.SetTranslation( aV01x );
5397
5398           // traslated point
5399           aV1x = aV0x.Transformed( aTrsf );
5400           aPN1 = aPN0.Transformed( aTrsf );
5401
5402           // rotation 1 [ T1,T0 ]
5403           aAngleT1T0=-aDT1x.Angle( aDT0x );
5404           if (fabs(aAngleT1T0) > aTolAng) {
5405             aDT1T0=aDT1x^aDT0x;
5406             anAxT1T0.SetLocation( aV1x );
5407             anAxT1T0.SetDirection( aDT1T0 );
5408             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5409
5410             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5411           }
5412
5413           // rotation 2
5414           if ( theHasAngles ) {
5415             anAx1.SetLocation( aV1x );
5416             anAx1.SetDirection( aDT1x );
5417             aTrsfRot.SetRotation( anAx1, aAngle1x );
5418
5419             aPN1 = aPN1.Transformed( aTrsfRot );
5420           }
5421
5422           // make new node
5423           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5424           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5425             // create additional node
5426             double x = ( aPN1.X() + aPN0.X() )/2.;
5427             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5428             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5429             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5430             myLastCreatedNodes.Append(newNode);
5431             srcNodes.Append( node );
5432             listNewNodes.push_back( newNode );
5433           }
5434           aX = aPN1.X();
5435           aY = aPN1.Y();
5436           aZ = aPN1.Z();
5437           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5438           myLastCreatedNodes.Append(newNode);
5439           srcNodes.Append( node );
5440           listNewNodes.push_back( newNode );
5441
5442           aPN0 = aPN1;
5443           aP0x = aP1x;
5444           aV0x = aV1x;
5445           aDT0x = aDT1x;
5446         }
5447       }
5448
5449       else {
5450         // if current elem is quadratic and current node is not medium
5451         // we have to check - may be it is needed to insert additional nodes
5452         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5453           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5454           if(listNewNodes.size()==aNbTP-1) {
5455             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5456             gp_XYZ P(node->X(), node->Y(), node->Z());
5457             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5458             int i;
5459             for(i=0; i<aNbTP-1; i++) {
5460               const SMDS_MeshNode* N = *it;
5461               double x = ( N->X() + P.X() )/2.;
5462               double y = ( N->Y() + P.Y() )/2.;
5463               double z = ( N->Z() + P.Z() )/2.;
5464               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5465               srcNodes.Append( node );
5466               myLastCreatedNodes.Append(newN);
5467               aNodes[2*i] = newN;
5468               aNodes[2*i+1] = N;
5469               P = gp_XYZ(N->X(),N->Y(),N->Z());
5470             }
5471             listNewNodes.clear();
5472             for(i=0; i<2*(aNbTP-1); i++) {
5473               listNewNodes.push_back(aNodes[i]);
5474             }
5475           }
5476         }
5477       }
5478
5479       newNodesItVec.push_back( nIt );
5480     }
5481     // make new elements
5482     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5483     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5484     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5485   }
5486
5487   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5488
5489   if ( theMakeGroups )
5490     generateGroups( srcNodes, srcElems, "extruded");
5491
5492   return EXTR_OK;
5493 }
5494
5495
5496 //=======================================================================
5497 //function : LinearAngleVariation
5498 //purpose  : auxilary for ExtrusionAlongTrack
5499 //=======================================================================
5500 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5501                                             list<double>& Angles)
5502 {
5503   int nbAngles = Angles.size();
5504   if( nbSteps > nbAngles ) {
5505     vector<double> theAngles(nbAngles);
5506     list<double>::iterator it = Angles.begin();
5507     int i = -1;
5508     for(; it!=Angles.end(); it++) {
5509       i++;
5510       theAngles[i] = (*it);
5511     }
5512     list<double> res;
5513     double rAn2St = double( nbAngles ) / double( nbSteps );
5514     double angPrev = 0, angle;
5515     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5516       double angCur = rAn2St * ( iSt+1 );
5517       double angCurFloor  = floor( angCur );
5518       double angPrevFloor = floor( angPrev );
5519       if ( angPrevFloor == angCurFloor )
5520         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5521       else {
5522         int iP = int( angPrevFloor );
5523         double angPrevCeil = ceil(angPrev);
5524         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5525
5526         int iC = int( angCurFloor );
5527         if ( iC < nbAngles )
5528           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5529
5530         iP = int( angPrevCeil );
5531         while ( iC-- > iP )
5532           angle += theAngles[ iC ];
5533       }
5534       res.push_back(angle);
5535       angPrev = angCur;
5536     }
5537     Angles.clear();
5538     it = res.begin();
5539     for(; it!=res.end(); it++)
5540       Angles.push_back( *it );
5541   }
5542 }
5543
5544
5545 //================================================================================
5546 /*!
5547  * \brief Move or copy theElements applying theTrsf to their nodes
5548  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5549  *  \param theTrsf - transformation to apply
5550  *  \param theCopy - if true, create translated copies of theElems
5551  *  \param theMakeGroups - if true and theCopy, create translated groups
5552  *  \param theTargetMesh - mesh to copy translated elements into
5553  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5554  */
5555 //================================================================================
5556
5557 SMESH_MeshEditor::PGroupIDs
5558 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5559                              const gp_Trsf&     theTrsf,
5560                              const bool         theCopy,
5561                              const bool         theMakeGroups,
5562                              SMESH_Mesh*        theTargetMesh)
5563 {
5564   myLastCreatedElems.Clear();
5565   myLastCreatedNodes.Clear();
5566
5567   bool needReverse = false;
5568   string groupPostfix;
5569   switch ( theTrsf.Form() ) {
5570   case gp_PntMirror:
5571     MESSAGE("gp_PntMirror");
5572     needReverse = true;
5573     groupPostfix = "mirrored";
5574     break;
5575   case gp_Ax1Mirror:
5576     MESSAGE("gp_Ax1Mirror");
5577     groupPostfix = "mirrored";
5578     break;
5579   case gp_Ax2Mirror:
5580     MESSAGE("gp_Ax2Mirror");
5581     needReverse = true;
5582     groupPostfix = "mirrored";
5583     break;
5584   case gp_Rotation:
5585     MESSAGE("gp_Rotation");
5586     groupPostfix = "rotated";
5587     break;
5588   case gp_Translation:
5589     MESSAGE("gp_Translation");
5590     groupPostfix = "translated";
5591     break;
5592   case gp_Scale:
5593     MESSAGE("gp_Scale");
5594     groupPostfix = "scaled";
5595     break;
5596   case gp_CompoundTrsf: // different scale by axis
5597     MESSAGE("gp_CompoundTrsf");
5598     groupPostfix = "scaled";
5599     break;
5600   default:
5601     MESSAGE("default");
5602     needReverse = false;
5603     groupPostfix = "transformed";
5604   }
5605
5606   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5607   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5608   SMESHDS_Mesh* aMesh    = GetMeshDS();
5609
5610
5611   // map old node to new one
5612   TNodeNodeMap nodeMap;
5613
5614   // elements sharing moved nodes; those of them which have all
5615   // nodes mirrored but are not in theElems are to be reversed
5616   TIDSortedElemSet inverseElemSet;
5617
5618   // source elements for each generated one
5619   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5620
5621   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5622   TIDSortedElemSet orphanNode;
5623
5624   if ( theElems.empty() ) // transform the whole mesh
5625   {
5626     // add all elements
5627     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5628     while ( eIt->more() ) theElems.insert( eIt->next() );
5629     // add orphan nodes
5630     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5631     while ( nIt->more() )
5632     {
5633       const SMDS_MeshNode* node = nIt->next();
5634       if ( node->NbInverseElements() == 0)
5635         orphanNode.insert( node );
5636     }
5637   }
5638
5639   // loop on elements to transform nodes : first orphan nodes then elems
5640   TIDSortedElemSet::iterator itElem;
5641   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5642   for (int i=0; i<2; i++)
5643   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5644     const SMDS_MeshElement* elem = *itElem;
5645     if ( !elem )
5646       continue;
5647
5648     // loop on elem nodes
5649     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5650     while ( itN->more() ) {
5651
5652       const SMDS_MeshNode* node = cast2Node( itN->next() );
5653       // check if a node has been already transformed
5654       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5655         nodeMap.insert( make_pair ( node, node ));
5656       if ( !n2n_isnew.second )
5657         continue;
5658
5659       double coord[3];
5660       coord[0] = node->X();
5661       coord[1] = node->Y();
5662       coord[2] = node->Z();
5663       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5664       if ( theTargetMesh ) {
5665         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5666         n2n_isnew.first->second = newNode;
5667         myLastCreatedNodes.Append(newNode);
5668         srcNodes.Append( node );
5669       }
5670       else if ( theCopy ) {
5671         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5672         n2n_isnew.first->second = newNode;
5673         myLastCreatedNodes.Append(newNode);
5674         srcNodes.Append( node );
5675       }
5676       else {
5677         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5678         // node position on shape becomes invalid
5679         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5680           ( SMDS_SpacePosition::originSpacePosition() );
5681       }
5682
5683       // keep inverse elements
5684       if ( !theCopy && !theTargetMesh && needReverse ) {
5685         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5686         while ( invElemIt->more() ) {
5687           const SMDS_MeshElement* iel = invElemIt->next();
5688           inverseElemSet.insert( iel );
5689         }
5690       }
5691     }
5692   }
5693
5694   // either create new elements or reverse mirrored ones
5695   if ( !theCopy && !needReverse && !theTargetMesh )
5696     return PGroupIDs();
5697
5698   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5699   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5700     theElems.insert( *invElemIt );
5701
5702   // Replicate or reverse elements
5703
5704   std::vector<int> iForw;
5705   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5706   {
5707     const SMDS_MeshElement* elem = *itElem;
5708     if ( !elem ) continue;
5709
5710     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5711     int                  nbNodes  = elem->NbNodes();
5712     if ( geomType == SMDSGeom_POINT ) continue; // node
5713
5714     switch ( geomType ) {
5715
5716     case SMDSGeom_POLYGON:  // ---------------------- polygon
5717       {
5718         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5719         int iNode = 0;
5720         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5721         while (itN->more()) {
5722           const SMDS_MeshNode* node =
5723             static_cast<const SMDS_MeshNode*>(itN->next());
5724           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5725           if (nodeMapIt == nodeMap.end())
5726             break; // not all nodes transformed
5727           if (needReverse) {
5728             // reverse mirrored faces and volumes
5729             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5730           } else {
5731             poly_nodes[iNode] = (*nodeMapIt).second;
5732           }
5733           iNode++;
5734         }
5735         if ( iNode != nbNodes )
5736           continue; // not all nodes transformed
5737
5738         if ( theTargetMesh ) {
5739           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5740           srcElems.Append( elem );
5741         }
5742         else if ( theCopy ) {
5743           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5744           srcElems.Append( elem );
5745         }
5746         else {
5747           aMesh->ChangePolygonNodes(elem, poly_nodes);
5748         }
5749       }
5750       break;
5751
5752     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5753       {
5754         const SMDS_VtkVolume* aPolyedre =
5755           dynamic_cast<const SMDS_VtkVolume*>( elem );
5756         if (!aPolyedre) {
5757           MESSAGE("Warning: bad volumic element");
5758           continue;
5759         }
5760
5761         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5762         vector<int> quantities; quantities.reserve( nbNodes );
5763
5764         bool allTransformed = true;
5765         int nbFaces = aPolyedre->NbFaces();
5766         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5767           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5768           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5769             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5770             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5771             if (nodeMapIt == nodeMap.end()) {
5772               allTransformed = false; // not all nodes transformed
5773             } else {
5774               poly_nodes.push_back((*nodeMapIt).second);
5775             }
5776             if ( needReverse && allTransformed )
5777               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5778           }
5779           quantities.push_back(nbFaceNodes);
5780         }
5781         if ( !allTransformed )
5782           continue; // not all nodes transformed
5783
5784         if ( theTargetMesh ) {
5785           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5786           srcElems.Append( elem );
5787         }
5788         else if ( theCopy ) {
5789           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5790           srcElems.Append( elem );
5791         }
5792         else {
5793           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5794         }
5795       }
5796       break;
5797
5798     case SMDSGeom_BALL: // -------------------- Ball
5799       {
5800         if ( !theCopy && !theTargetMesh ) continue;
5801
5802         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5803         if (nodeMapIt == nodeMap.end())
5804           continue; // not all nodes transformed
5805
5806         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5807         if ( theTargetMesh ) {
5808           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5809           srcElems.Append( elem );
5810         }
5811         else {
5812           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5813           srcElems.Append( elem );
5814         }
5815       }
5816       break;
5817
5818     default: // ----------------------- Regular elements
5819
5820       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5821       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5822       const std::vector<int>& i = needReverse ? iRev : iForw;
5823
5824       // find transformed nodes
5825       vector<const SMDS_MeshNode*> nodes(nbNodes);
5826       int iNode = 0;
5827       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5828       while ( itN->more() ) {
5829         const SMDS_MeshNode* node =
5830           static_cast<const SMDS_MeshNode*>( itN->next() );
5831         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5832         if ( nodeMapIt == nodeMap.end() )
5833           break; // not all nodes transformed
5834         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5835       }
5836       if ( iNode != nbNodes )
5837         continue; // not all nodes transformed
5838
5839       if ( theTargetMesh ) {
5840         if ( SMDS_MeshElement* copy =
5841              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5842           myLastCreatedElems.Append( copy );
5843           srcElems.Append( elem );
5844         }
5845       }
5846       else if ( theCopy ) {
5847         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5848           srcElems.Append( elem );
5849       }
5850       else {
5851         // reverse element as it was reversed by transformation
5852         if ( nbNodes > 2 )
5853           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5854       }
5855     } // switch ( geomType )
5856
5857   } // loop on elements
5858
5859   PGroupIDs newGroupIDs;
5860
5861   if ( ( theMakeGroups && theCopy ) ||
5862        ( theMakeGroups && theTargetMesh ) )
5863     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5864
5865   return newGroupIDs;
5866 }
5867
5868 //=======================================================================
5869 /*!
5870  * \brief Create groups of elements made during transformation
5871  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5872  * \param elemGens - elements making corresponding myLastCreatedElems
5873  * \param postfix - to append to names of new groups
5874  */
5875 //=======================================================================
5876
5877 SMESH_MeshEditor::PGroupIDs
5878 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5879                                  const SMESH_SequenceOfElemPtr& elemGens,
5880                                  const std::string&             postfix,
5881                                  SMESH_Mesh*                    targetMesh)
5882 {
5883   PGroupIDs newGroupIDs( new list<int> );
5884   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5885
5886   // Sort existing groups by types and collect their names
5887
5888   // to store an old group and a generated new one
5889   typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5890   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5891   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5892   // group names
5893   set< string > groupNames;
5894   
5895   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5896   if ( !groupIt->more() ) return newGroupIDs;
5897
5898   int newGroupID = mesh->GetGroupIds().back()+1;
5899   while ( groupIt->more() )
5900   {
5901     SMESH_Group * group = groupIt->next();
5902     if ( !group ) continue;
5903     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5904     if ( !groupDS || groupDS->IsEmpty() ) continue;
5905     groupNames.insert( group->GetName() );
5906     groupDS->SetStoreName( group->GetName() );
5907     SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5908                                                  groupDS->GetType() );
5909     groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5910     orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5911   }
5912
5913   // Loop on nodes and elements to add them in new groups
5914
5915   for ( int isNodes = 0; isNodes < 2; ++isNodes )
5916   {
5917     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
5918     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5919     if ( gens.Length() != elems.Length() )
5920       throw SALOME_Exception(LOCALIZED("invalid args"));
5921
5922     // loop on created elements
5923     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5924     {
5925       const SMDS_MeshElement* sourceElem = gens( iElem );
5926       if ( !sourceElem ) {
5927         MESSAGE("generateGroups(): NULL source element");
5928         continue;
5929       }
5930       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5931       if ( groupsOldNew.empty() ) { // no groups of this type at all
5932         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5933           ++iElem; // skip all elements made by sourceElem
5934         continue;
5935       }
5936       // collect all elements made by sourceElem
5937       list< const SMDS_MeshElement* > resultElems;
5938       if ( const SMDS_MeshElement* resElem = elems( iElem ))
5939         if ( resElem != sourceElem )
5940           resultElems.push_back( resElem );
5941       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5942         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5943           if ( resElem != sourceElem )
5944             resultElems.push_back( resElem );
5945
5946       // add resultElems to groups made by ones the sourceElem belongs to
5947       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5948       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5949       {
5950         SMESHDS_GroupBase* oldGroup = gOldNew->first;
5951         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
5952         {
5953           // fill in a new group
5954           SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
5955           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5956           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5957             newGroup.Add( *resElemIt );
5958         }
5959       }
5960     } // loop on created elements
5961   }// loop on nodes and elements
5962
5963   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
5964
5965   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
5966   {
5967     SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
5968     SMESHDS_Group*     newGroupDS = orderedOldNewGroups[i]->second;
5969     if ( newGroupDS->IsEmpty() )
5970     {
5971       mesh->GetMeshDS()->RemoveGroup( newGroupDS );
5972     }
5973     else
5974     {
5975       // make a name
5976       string name = oldGroupDS->GetStoreName();
5977       if ( !targetMesh ) {
5978         name += "_";
5979         name += postfix;
5980         int nb = 1;
5981         while ( !groupNames.insert( name ).second ) // name exists
5982           name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
5983       }
5984       newGroupDS->SetStoreName( name.c_str() );
5985
5986       // make a SMESH_Groups
5987       mesh->AddGroup( newGroupDS );
5988       newGroupIDs->push_back( newGroupDS->GetID() );
5989     }
5990   }
5991
5992   return newGroupIDs;
5993 }
5994
5995 //================================================================================
5996 /*!
5997  * \brief Return list of group of nodes close to each other within theTolerance
5998  *        Search among theNodes or in the whole mesh if theNodes is empty using
5999  *        an Octree algorithm
6000  */
6001 //================================================================================
6002
6003 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6004                                             const double         theTolerance,
6005                                             TListOfListOfNodes & theGroupsOfNodes)
6006 {
6007   myLastCreatedElems.Clear();
6008   myLastCreatedNodes.Clear();
6009
6010   if ( theNodes.empty() )
6011   { // get all nodes in the mesh
6012     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6013     while ( nIt->more() )
6014       theNodes.insert( theNodes.end(),nIt->next());
6015   }
6016
6017   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6018 }
6019
6020
6021 //=======================================================================
6022 /*!
6023  * \brief Implementation of search for the node closest to point
6024  */
6025 //=======================================================================
6026
6027 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6028 {
6029   //---------------------------------------------------------------------
6030   /*!
6031    * \brief Constructor
6032    */
6033   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6034   {
6035     myMesh = ( SMESHDS_Mesh* ) theMesh;
6036
6037     TIDSortedNodeSet nodes;
6038     if ( theMesh ) {
6039       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6040       while ( nIt->more() )
6041         nodes.insert( nodes.end(), nIt->next() );
6042     }
6043     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6044
6045     // get max size of a leaf box
6046     SMESH_OctreeNode* tree = myOctreeNode;
6047     while ( !tree->isLeaf() )
6048     {
6049       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6050       if ( cIt->more() )
6051         tree = cIt->next();
6052     }
6053     myHalfLeafSize = tree->maxSize() / 2.;
6054   }
6055
6056   //---------------------------------------------------------------------
6057   /*!
6058    * \brief Move node and update myOctreeNode accordingly
6059    */
6060   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6061   {
6062     myOctreeNode->UpdateByMoveNode( node, toPnt );
6063     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6064   }
6065
6066   //---------------------------------------------------------------------
6067   /*!
6068    * \brief Do it's job
6069    */
6070   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6071   {
6072     map<double, const SMDS_MeshNode*> dist2Nodes;
6073     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6074     if ( !dist2Nodes.empty() )
6075       return dist2Nodes.begin()->second;
6076     list<const SMDS_MeshNode*> nodes;
6077     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6078
6079     double minSqDist = DBL_MAX;
6080     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6081     {
6082       // sort leafs by their distance from thePnt
6083       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6084       TDistTreeMap treeMap;
6085       list< SMESH_OctreeNode* > treeList;
6086       list< SMESH_OctreeNode* >::iterator trIt;
6087       treeList.push_back( myOctreeNode );
6088
6089       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6090       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6091       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6092       {
6093         SMESH_OctreeNode* tree = *trIt;
6094         if ( !tree->isLeaf() ) // put children to the queue
6095         {
6096           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6097           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6098           while ( cIt->more() )
6099             treeList.push_back( cIt->next() );
6100         }
6101         else if ( tree->NbNodes() ) // put a tree to the treeMap
6102         {
6103           const Bnd_B3d& box = tree->getBox();
6104           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6105           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6106           if ( !it_in.second ) // not unique distance to box center
6107             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6108         }
6109       }
6110       // find distance after which there is no sense to check tree's
6111       double sqLimit = DBL_MAX;
6112       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6113       if ( treeMap.size() > 5 ) {
6114         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6115         const Bnd_B3d& box = closestTree->getBox();
6116         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6117         sqLimit = limit * limit;
6118       }
6119       // get all nodes from trees
6120       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6121         if ( sqDist_tree->first > sqLimit )
6122           break;
6123         SMESH_OctreeNode* tree = sqDist_tree->second;
6124         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6125       }
6126     }
6127     // find closest among nodes
6128     minSqDist = DBL_MAX;
6129     const SMDS_MeshNode* closestNode = 0;
6130     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6131     for ( ; nIt != nodes.end(); ++nIt ) {
6132       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6133       if ( minSqDist > sqDist ) {
6134         closestNode = *nIt;
6135         minSqDist = sqDist;
6136       }
6137     }
6138     return closestNode;
6139   }
6140
6141   //---------------------------------------------------------------------
6142   /*!
6143    * \brief Destructor
6144    */
6145   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6146
6147   //---------------------------------------------------------------------
6148   /*!
6149    * \brief Return the node tree
6150    */
6151   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6152
6153 private:
6154   SMESH_OctreeNode* myOctreeNode;
6155   SMESHDS_Mesh*     myMesh;
6156   double            myHalfLeafSize; // max size of a leaf box
6157 };
6158
6159 //=======================================================================
6160 /*!
6161  * \brief Return SMESH_NodeSearcher
6162  */
6163 //=======================================================================
6164
6165 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher() 
6166 {
6167   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6168 }
6169
6170 // ========================================================================
6171 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6172 {
6173   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6174   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6175   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6176
6177   //=======================================================================
6178   /*!
6179    * \brief Octal tree of bounding boxes of elements
6180    */
6181   //=======================================================================
6182
6183   class ElementBndBoxTree : public SMESH_Octree
6184   {
6185   public:
6186
6187     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6188                       SMDSAbs_ElementType  elemType,
6189                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6190                       double               tolerance = NodeRadius );
6191     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6192     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6193     void getElementsInSphere ( const gp_XYZ& center,
6194                                const double  radius, TIDSortedElemSet& foundElems);
6195     size_t getSize() { return std::max( _size, _elements.size() ); }
6196     ~ElementBndBoxTree();
6197
6198   protected:
6199     ElementBndBoxTree():_size(0) {}
6200     SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6201     void          buildChildrenData();
6202     Bnd_B3d*      buildRootBox();
6203   private:
6204     //!< Bounding box of element
6205     struct ElementBox : public Bnd_B3d
6206     {
6207       const SMDS_MeshElement* _element;
6208       int                     _refCount; // an ElementBox can be included in several tree branches
6209       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6210     };
6211     vector< ElementBox* > _elements;
6212     size_t                _size;
6213   };
6214
6215   //================================================================================
6216   /*!
6217    * \brief ElementBndBoxTree creation
6218    */
6219   //================================================================================
6220
6221   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6222     :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6223   {
6224     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6225     _elements.reserve( nbElems );
6226
6227     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6228     while ( elemIt->more() )
6229       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6230
6231     compute();
6232   }
6233
6234   //================================================================================
6235   /*!
6236    * \brief Destructor
6237    */
6238   //================================================================================
6239
6240   ElementBndBoxTree::~ElementBndBoxTree()
6241   {
6242     for ( int i = 0; i < _elements.size(); ++i )
6243       if ( --_elements[i]->_refCount <= 0 )
6244         delete _elements[i];
6245   }
6246
6247   //================================================================================
6248   /*!
6249    * \brief Return the maximal box
6250    */
6251   //================================================================================
6252
6253   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6254   {
6255     Bnd_B3d* box = new Bnd_B3d;
6256     for ( int i = 0; i < _elements.size(); ++i )
6257       box->Add( *_elements[i] );
6258     return box;
6259   }
6260
6261   //================================================================================
6262   /*!
6263    * \brief Redistrubute element boxes among children
6264    */
6265   //================================================================================
6266
6267   void ElementBndBoxTree::buildChildrenData()
6268   {
6269     for ( int i = 0; i < _elements.size(); ++i )
6270     {
6271       for (int j = 0; j < 8; j++)
6272       {
6273         if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6274         {
6275           _elements[i]->_refCount++;
6276           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6277         }
6278       }
6279       _elements[i]->_refCount--;
6280     }
6281     _size = _elements.size();
6282     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6283
6284     for (int j = 0; j < 8; j++)
6285     {
6286       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6287       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6288         child->myIsLeaf = true;
6289
6290       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6291         SMESHUtils::CompactVector( child->_elements );
6292     }
6293   }
6294
6295   //================================================================================
6296   /*!
6297    * \brief Return elements which can include the point
6298    */
6299   //================================================================================
6300
6301   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6302                                                 TIDSortedElemSet& foundElems)
6303   {
6304     if ( getBox().IsOut( point.XYZ() ))
6305       return;
6306
6307     if ( isLeaf() )
6308     {
6309       for ( int i = 0; i < _elements.size(); ++i )
6310         if ( !_elements[i]->IsOut( point.XYZ() ))
6311           foundElems.insert( _elements[i]->_element );
6312     }
6313     else
6314     {
6315       for (int i = 0; i < 8; i++)
6316         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6317     }
6318   }
6319
6320   //================================================================================
6321   /*!
6322    * \brief Return elements which can be intersected by the line
6323    */
6324   //================================================================================
6325
6326   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6327                                                TIDSortedElemSet& foundElems)
6328   {
6329     if ( getBox().IsOut( line ))
6330       return;
6331
6332     if ( isLeaf() )
6333     {
6334       for ( int i = 0; i < _elements.size(); ++i )
6335         if ( !_elements[i]->IsOut( line ))
6336           foundElems.insert( _elements[i]->_element );
6337     }
6338     else
6339     {
6340       for (int i = 0; i < 8; i++)
6341         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6342     }
6343   }
6344
6345   //================================================================================
6346   /*!
6347    * \brief Return elements from leaves intersecting the sphere
6348    */
6349   //================================================================================
6350
6351   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6352                                                 const double      radius,
6353                                                 TIDSortedElemSet& foundElems)
6354   {
6355     if ( getBox().IsOut( center, radius ))
6356       return;
6357
6358     if ( isLeaf() )
6359     {
6360       for ( int i = 0; i < _elements.size(); ++i )
6361         if ( !_elements[i]->IsOut( center, radius ))
6362           foundElems.insert( _elements[i]->_element );
6363     }
6364     else
6365     {
6366       for (int i = 0; i < 8; i++)
6367         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6368     }
6369   }
6370
6371   //================================================================================
6372   /*!
6373    * \brief Construct the element box
6374    */
6375   //================================================================================
6376
6377   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6378   {
6379     _element  = elem;
6380     _refCount = 1;
6381     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6382     while ( nIt->more() )
6383       Add( SMESH_TNodeXYZ( nIt->next() ));
6384     Enlarge( tolerance );
6385   }
6386
6387 } // namespace
6388
6389 //=======================================================================
6390 /*!
6391  * \brief Implementation of search for the elements by point and
6392  *        of classification of point in 2D mesh
6393  */
6394 //=======================================================================
6395
6396 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6397 {
6398   SMESHDS_Mesh*                _mesh;
6399   SMDS_ElemIteratorPtr         _meshPartIt;
6400   ElementBndBoxTree*           _ebbTree;
6401   SMESH_NodeSearcherImpl*      _nodeSearcher;
6402   SMDSAbs_ElementType          _elementType;
6403   double                       _tolerance;
6404   bool                         _outerFacesFound;
6405   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6406
6407   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6408     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6409   ~SMESH_ElementSearcherImpl()
6410   {
6411     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6412     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6413   }
6414   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6415                                   SMDSAbs_ElementType                type,
6416                                   vector< const SMDS_MeshElement* >& foundElements);
6417   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6418   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6419                                                  SMDSAbs_ElementType type );
6420
6421   void GetElementsNearLine( const gp_Ax1&                      line,
6422                             SMDSAbs_ElementType                type,
6423                             vector< const SMDS_MeshElement* >& foundElems);
6424   double getTolerance();
6425   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6426                             const double tolerance, double & param);
6427   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6428   bool isOuterBoundary(const SMDS_MeshElement* face) const
6429   {
6430     return _outerFaces.empty() || _outerFaces.count(face);
6431   }
6432   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6433   {
6434     const SMDS_MeshElement* _face;
6435     gp_Vec                  _faceNorm;
6436     bool                    _coincides; //!< the line lays in face plane
6437     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6438       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6439   };
6440   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6441   {
6442     SMESH_TLink      _link;
6443     TIDSortedElemSet _faces;
6444     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6445       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6446   };
6447 };
6448
6449 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6450 {
6451   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6452              << ", _coincides="<<i._coincides << ")";
6453 }
6454
6455 //=======================================================================
6456 /*!
6457  * \brief define tolerance for search
6458  */
6459 //=======================================================================
6460
6461 double SMESH_ElementSearcherImpl::getTolerance()
6462 {
6463   if ( _tolerance < 0 )
6464   {
6465     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6466
6467     _tolerance = 0;
6468     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6469     {
6470       double boxSize = _nodeSearcher->getTree()->maxSize();
6471       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6472     }
6473     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6474     {
6475       double boxSize = _ebbTree->maxSize();
6476       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6477     }
6478     if ( _tolerance == 0 )
6479     {
6480       // define tolerance by size of a most complex element
6481       int complexType = SMDSAbs_Volume;
6482       while ( complexType > SMDSAbs_All &&
6483               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6484         --complexType;
6485       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6486       double elemSize;
6487       if ( complexType == int( SMDSAbs_Node ))
6488       {
6489         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6490         elemSize = 1;
6491         if ( meshInfo.NbNodes() > 2 )
6492           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6493       }
6494       else
6495       {
6496         SMDS_ElemIteratorPtr elemIt =
6497             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6498         const SMDS_MeshElement* elem = elemIt->next();
6499         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6500         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6501         elemSize = 0;
6502         while ( nodeIt->more() )
6503         {
6504           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6505           elemSize = max( dist, elemSize );
6506         }
6507       }
6508       _tolerance = 1e-4 * elemSize;
6509     }
6510   }
6511   return _tolerance;
6512 }
6513
6514 //================================================================================
6515 /*!
6516  * \brief Find intersection of the line and an edge of face and return parameter on line
6517  */
6518 //================================================================================
6519
6520 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6521                                                      const SMDS_MeshElement* face,
6522                                                      const double            tol,
6523                                                      double &                param)
6524 {
6525   int nbInts = 0;
6526   param = 0;
6527
6528   GeomAPI_ExtremaCurveCurve anExtCC;
6529   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6530   
6531   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6532   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6533   {
6534     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6535                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) )); 
6536     anExtCC.Init( lineCurve, edge);
6537     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6538     {
6539       Quantity_Parameter pl, pe;
6540       anExtCC.LowerDistanceParameters( pl, pe );
6541       param += pl;
6542       if ( ++nbInts == 2 )
6543         break;
6544     }
6545   }
6546   if ( nbInts > 0 ) param /= nbInts;
6547   return nbInts > 0;
6548 }
6549 //================================================================================
6550 /*!
6551  * \brief Find all faces belonging to the outer boundary of mesh
6552  */
6553 //================================================================================
6554
6555 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6556 {
6557   if ( _outerFacesFound ) return;
6558
6559   // Collect all outer faces by passing from one outer face to another via their links
6560   // and BTW find out if there are internal faces at all.
6561
6562   // checked links and links where outer boundary meets internal one
6563   set< SMESH_TLink > visitedLinks, seamLinks;
6564
6565   // links to treat with already visited faces sharing them
6566   list < TFaceLink > startLinks;
6567
6568   // load startLinks with the first outerFace
6569   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6570   _outerFaces.insert( outerFace );
6571
6572   TIDSortedElemSet emptySet;
6573   while ( !startLinks.empty() )
6574   {
6575     const SMESH_TLink& link  = startLinks.front()._link;
6576     TIDSortedElemSet&  faces = startLinks.front()._faces;
6577
6578     outerFace = *faces.begin();
6579     // find other faces sharing the link
6580     const SMDS_MeshElement* f;
6581     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6582       faces.insert( f );
6583
6584     // select another outer face among the found 
6585     const SMDS_MeshElement* outerFace2 = 0;
6586     if ( faces.size() == 2 )
6587     {
6588       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6589     }
6590     else if ( faces.size() > 2 )
6591     {
6592       seamLinks.insert( link );
6593
6594       // link direction within the outerFace
6595       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6596                    SMESH_TNodeXYZ( link.node2()));
6597       int i1 = outerFace->GetNodeIndex( link.node1() );
6598       int i2 = outerFace->GetNodeIndex( link.node2() );
6599       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6600       if ( rev ) n1n2.Reverse();
6601       // outerFace normal
6602       gp_XYZ ofNorm, fNorm;
6603       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6604       {
6605         // direction from the link inside outerFace
6606         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6607         // sort all other faces by angle with the dirInOF
6608         map< double, const SMDS_MeshElement* > angle2Face;
6609         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6610         for ( ; face != faces.end(); ++face )
6611         {
6612           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6613             continue;
6614           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6615           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6616           if ( angle < 0 ) angle += 2. * M_PI;
6617           angle2Face.insert( make_pair( angle, *face ));
6618         }
6619         if ( !angle2Face.empty() )
6620           outerFace2 = angle2Face.begin()->second;
6621       }
6622     }
6623     // store the found outer face and add its links to continue seaching from
6624     if ( outerFace2 )
6625     {
6626       _outerFaces.insert( outerFace );
6627       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6628       for ( int i = 0; i < nbNodes; ++i )
6629       {
6630         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6631         if ( visitedLinks.insert( link2 ).second )
6632           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6633       }
6634     }
6635     startLinks.pop_front();
6636   }
6637   _outerFacesFound = true;
6638
6639   if ( !seamLinks.empty() )
6640   {
6641     // There are internal boundaries touching the outher one,
6642     // find all faces of internal boundaries in order to find
6643     // faces of boundaries of holes, if any.
6644     
6645   }
6646   else
6647   {
6648     _outerFaces.clear();
6649   }
6650 }
6651
6652 //=======================================================================
6653 /*!
6654  * \brief Find elements of given type where the given point is IN or ON.
6655  *        Returns nb of found elements and elements them-selves.
6656  *
6657  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements 
6658  */
6659 //=======================================================================
6660
6661 int SMESH_ElementSearcherImpl::
6662 FindElementsByPoint(const gp_Pnt&                      point,
6663                     SMDSAbs_ElementType                type,
6664                     vector< const SMDS_MeshElement* >& foundElements)
6665 {
6666   foundElements.clear();
6667
6668   double tolerance = getTolerance();
6669
6670   // =================================================================================
6671   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6672   {
6673     if ( !_nodeSearcher )
6674       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6675
6676     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6677     if ( !closeNode ) return foundElements.size();
6678
6679     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6680       return foundElements.size(); // to far from any node
6681
6682     if ( type == SMDSAbs_Node )
6683     {
6684       foundElements.push_back( closeNode );
6685     }
6686     else
6687     {
6688       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6689       while ( elemIt->more() )
6690         foundElements.push_back( elemIt->next() );
6691     }
6692   }
6693   // =================================================================================
6694   else // elements more complex than 0D
6695   {
6696     if ( !_ebbTree || _elementType != type )
6697     {
6698       if ( _ebbTree ) delete _ebbTree;
6699       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6700     }
6701     TIDSortedElemSet suspectElems;
6702     _ebbTree->getElementsNearPoint( point, suspectElems );
6703     TIDSortedElemSet::iterator elem = suspectElems.begin();
6704     for ( ; elem != suspectElems.end(); ++elem )
6705       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6706         foundElements.push_back( *elem );
6707   }
6708   return foundElements.size();
6709 }
6710
6711 //=======================================================================
6712 /*!
6713  * \brief Find an element of given type most close to the given point
6714  *
6715  * WARNING: Only face search is implemeneted so far
6716  */
6717 //=======================================================================
6718
6719 const SMDS_MeshElement*
6720 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6721                                           SMDSAbs_ElementType type )
6722 {
6723   const SMDS_MeshElement* closestElem = 0;
6724
6725   if ( type == SMDSAbs_Face )
6726   {
6727     if ( !_ebbTree || _elementType != type )
6728     {
6729       if ( _ebbTree ) delete _ebbTree;
6730       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6731     }
6732     TIDSortedElemSet suspectElems;
6733     _ebbTree->getElementsNearPoint( point, suspectElems );
6734     
6735     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6736     {
6737       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox().CornerMin() +
6738                                  _ebbTree->getBox().CornerMax() );
6739       double radius;
6740       if ( _ebbTree->getBox().IsOut( point.XYZ() ))
6741         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6742       else
6743         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6744       while ( suspectElems.empty() )
6745       {
6746         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6747         radius *= 1.1;
6748       }
6749     }
6750     double minDist = std::numeric_limits<double>::max();
6751     multimap< double, const SMDS_MeshElement* > dist2face;
6752     TIDSortedElemSet::iterator elem = suspectElems.begin();
6753     for ( ; elem != suspectElems.end(); ++elem )
6754     {
6755       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6756                                                    point );
6757       if ( dist < minDist + 1e-10)
6758       {
6759         minDist = dist;
6760         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6761       }
6762     }
6763     if ( !dist2face.empty() )
6764     {
6765       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6766       closestElem = d2f->second;
6767       // if there are several elements at the same distance, select one
6768       // with GC closest to the point
6769       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6770       double minDistToGC = 0;
6771       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6772       {
6773         if ( minDistToGC == 0 )
6774         {
6775           gp_XYZ gc(0,0,0);
6776           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6777                            TXyzIterator(), gc ) / closestElem->NbNodes();
6778           minDistToGC = point.SquareDistance( gc );
6779         }
6780         gp_XYZ gc(0,0,0);
6781         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6782                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6783         double d = point.SquareDistance( gc );
6784         if ( d < minDistToGC )
6785         {
6786           minDistToGC = d;
6787           closestElem = d2f->second;
6788         }
6789       }
6790       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6791       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6792     }
6793   }
6794   else
6795   {
6796     // NOT IMPLEMENTED SO FAR
6797   }
6798   return closestElem;
6799 }
6800
6801
6802 //================================================================================
6803 /*!
6804  * \brief Classify the given point in the closed 2D mesh
6805  */
6806 //================================================================================
6807
6808 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6809 {
6810   double tolerance = getTolerance();
6811   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6812   {
6813     if ( _ebbTree ) delete _ebbTree;
6814     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6815   }
6816   // Algo: analyse transition of a line starting at the point through mesh boundary;
6817   // try three lines parallel to axis of the coordinate system and perform rough
6818   // analysis. If solution is not clear perform thorough analysis.
6819
6820   const int nbAxes = 3;
6821   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6822   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6823   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6824   multimap< int, int > nbInt2Axis; // to find the simplest case
6825   for ( int axis = 0; axis < nbAxes; ++axis )
6826   {
6827     gp_Ax1 lineAxis( point, axisDir[axis]);
6828     gp_Lin line    ( lineAxis );
6829
6830     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6831     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6832
6833     // Intersect faces with the line
6834
6835     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6836     TIDSortedElemSet::iterator face = suspectFaces.begin();
6837     for ( ; face != suspectFaces.end(); ++face )
6838     {
6839       // get face plane
6840       gp_XYZ fNorm;
6841       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6842       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6843
6844       // perform intersection
6845       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6846       if ( !intersection.IsDone() )
6847         continue;
6848       if ( intersection.IsInQuadric() )
6849       {
6850         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6851       }
6852       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6853       {
6854         gp_Pnt intersectionPoint = intersection.Point(1);
6855         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6856           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6857       }
6858     }
6859     // Analyse intersections roughly
6860
6861     int nbInter = u2inters.size();
6862     if ( nbInter == 0 )
6863       return TopAbs_OUT; 
6864
6865     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6866     if ( nbInter == 1 ) // not closed mesh
6867       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6868
6869     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6870       return TopAbs_ON;
6871
6872     if ( (f<0) == (l<0) )
6873       return TopAbs_OUT;
6874
6875     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6876     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
6877     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6878       return TopAbs_IN;
6879
6880     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6881
6882     if ( _outerFacesFound ) break; // pass to thorough analysis
6883
6884   } // three attempts - loop on CS axes
6885
6886   // Analyse intersections thoroughly.
6887   // We make two loops maximum, on the first one we only exclude touching intersections,
6888   // on the second, if situation is still unclear, we gather and use information on
6889   // position of faces (internal or outer). If faces position is already gathered,
6890   // we make the second loop right away.
6891
6892   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6893   {
6894     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6895     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6896     {
6897       int axis = nb_axis->second;
6898       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6899
6900       gp_Ax1 lineAxis( point, axisDir[axis]);
6901       gp_Lin line    ( lineAxis );
6902
6903       // add tangent intersections to u2inters
6904       double param;
6905       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6906       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6907         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6908           u2inters.insert(make_pair( param, *tgtInt ));
6909       tangentInters[ axis ].clear();
6910
6911       // Count intersections before and after the point excluding touching ones.
6912       // If hasPositionInfo we count intersections of outer boundary only
6913
6914       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6915       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6916       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6917       bool ok = ! u_int1->second._coincides;
6918       while ( ok && u_int1 != u2inters.end() )
6919       {
6920         double u = u_int1->first;
6921         bool touchingInt = false;
6922         if ( ++u_int2 != u2inters.end() )
6923         {
6924           // skip intersections at the same point (if the line passes through edge or node)
6925           int nbSamePnt = 0;
6926           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6927           {
6928             ++nbSamePnt;
6929             ++u_int2;
6930           }
6931
6932           // skip tangent intersections
6933           int nbTgt = 0;
6934           const SMDS_MeshElement* prevFace = u_int1->second._face;
6935           while ( ok && u_int2->second._coincides )
6936           {
6937             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6938               ok = false;
6939             else
6940             {
6941               nbTgt++;
6942               u_int2++;
6943               ok = ( u_int2 != u2inters.end() );
6944             }
6945           }
6946           if ( !ok ) break;
6947
6948           // skip intersections at the same point after tangent intersections
6949           if ( nbTgt > 0 )
6950           {
6951             double u2 = u_int2->first;
6952             ++u_int2;
6953             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6954             {
6955               ++nbSamePnt;
6956               ++u_int2;
6957             }
6958           }
6959           // decide if we skipped a touching intersection
6960           if ( nbSamePnt + nbTgt > 0 )
6961           {
6962             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6963             map< double, TInters >::iterator u_int = u_int1;
6964             for ( ; u_int != u_int2; ++u_int )
6965             {
6966               if ( u_int->second._coincides ) continue;
6967               double dot = u_int->second._faceNorm * line.Direction();
6968               if ( dot > maxDot ) maxDot = dot;
6969               if ( dot < minDot ) minDot = dot;
6970             }
6971             touchingInt = ( minDot*maxDot < 0 );
6972           }
6973         }
6974         if ( !touchingInt )
6975         {
6976           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6977           {
6978             if ( u < 0 )
6979               ++nbIntBeforePoint;
6980             else
6981               ++nbIntAfterPoint;
6982           }
6983           if ( u < f ) f = u;
6984           if ( u > l ) l = u;
6985         }
6986
6987         u_int1 = u_int2; // to next intersection
6988
6989       } // loop on intersections with one line
6990
6991       if ( ok )
6992       {
6993         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6994           return TopAbs_ON;
6995
6996         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
6997           return TopAbs_OUT; 
6998
6999         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7000           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7001
7002         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7003           return TopAbs_IN;
7004
7005         if ( (f<0) == (l<0) )
7006           return TopAbs_OUT;
7007
7008         if ( hasPositionInfo )
7009           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7010       }
7011     } // loop on intersections of the tree lines - thorough analysis
7012
7013     if ( !hasPositionInfo )
7014     {
7015       // gather info on faces position - is face in the outer boundary or not
7016       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7017       findOuterBoundary( u2inters.begin()->second._face );
7018     }
7019
7020   } // two attempts - with and w/o faces position info in the mesh
7021
7022   return TopAbs_UNKNOWN;
7023 }
7024
7025 //=======================================================================
7026 /*!
7027  * \brief Return elements possibly intersecting the line
7028  */
7029 //=======================================================================
7030
7031 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7032                                                      SMDSAbs_ElementType                type,
7033                                                      vector< const SMDS_MeshElement* >& foundElems)
7034 {
7035   if ( !_ebbTree || _elementType != type )
7036   {
7037     if ( _ebbTree ) delete _ebbTree;
7038     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7039   }
7040   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7041   _ebbTree->getElementsNearLine( line, suspectFaces );
7042   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7043 }
7044
7045 //=======================================================================
7046 /*!
7047  * \brief Return SMESH_ElementSearcher
7048  */
7049 //=======================================================================
7050
7051 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7052 {
7053   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7054 }
7055
7056 //=======================================================================
7057 /*!
7058  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7059  */
7060 //=======================================================================
7061
7062 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7063 {
7064   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7065 }
7066
7067 //=======================================================================
7068 /*!
7069  * \brief Return true if the point is IN or ON of the element
7070  */
7071 //=======================================================================
7072
7073 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7074 {
7075   if ( element->GetType() == SMDSAbs_Volume)
7076   {
7077     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7078   }
7079
7080   // get ordered nodes
7081
7082   vector< gp_XYZ > xyz;
7083   vector<const SMDS_MeshNode*> nodeList;
7084
7085   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7086   if ( element->IsQuadratic() ) {
7087     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7088       nodeIt = f->interlacedNodesElemIterator();
7089     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7090       nodeIt = e->interlacedNodesElemIterator();
7091   }
7092   while ( nodeIt->more() )
7093     {
7094       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7095       xyz.push_back( SMESH_TNodeXYZ(node) );
7096       nodeList.push_back(node);
7097     }
7098
7099   int i, nbNodes = element->NbNodes();
7100
7101   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7102   {
7103     // compute face normal
7104     gp_Vec faceNorm(0,0,0);
7105     xyz.push_back( xyz.front() );
7106     nodeList.push_back( nodeList.front() );
7107     for ( i = 0; i < nbNodes; ++i )
7108     {
7109       gp_Vec edge1( xyz[i+1], xyz[i]);
7110       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7111       faceNorm += edge1 ^ edge2;
7112     }
7113     double normSize = faceNorm.Magnitude();
7114     if ( normSize <= tol )
7115     {
7116       // degenerated face: point is out if it is out of all face edges
7117       for ( i = 0; i < nbNodes; ++i )
7118       {
7119         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7120         if ( !IsOut( &edge, point, tol ))
7121           return false;
7122       }
7123       return true;
7124     }
7125     faceNorm /= normSize;
7126
7127     // check if the point lays on face plane
7128     gp_Vec n2p( xyz[0], point );
7129     if ( fabs( n2p * faceNorm ) > tol )
7130       return true; // not on face plane
7131
7132     // check if point is out of face boundary:
7133     // define it by closest transition of a ray point->infinity through face boundary
7134     // on the face plane.
7135     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7136     // to find intersections of the ray with the boundary.
7137     gp_Vec ray = n2p;
7138     gp_Vec plnNorm = ray ^ faceNorm;
7139     normSize = plnNorm.Magnitude();
7140     if ( normSize <= tol ) return false; // point coincides with the first node
7141     plnNorm /= normSize;
7142     // for each node of the face, compute its signed distance to the plane
7143     vector<double> dist( nbNodes + 1);
7144     for ( i = 0; i < nbNodes; ++i )
7145     {
7146       gp_Vec n2p( xyz[i], point );
7147       dist[i] = n2p * plnNorm;
7148     }
7149     dist.back() = dist.front();
7150     // find the closest intersection
7151     int    iClosest = -1;
7152     double rClosest, distClosest = 1e100;;
7153     gp_Pnt pClosest;
7154     for ( i = 0; i < nbNodes; ++i )
7155     {
7156       double r;
7157       if ( fabs( dist[i]) < tol )
7158         r = 0.;
7159       else if ( fabs( dist[i+1]) < tol )
7160         r = 1.;
7161       else if ( dist[i] * dist[i+1] < 0 )
7162         r = dist[i] / ( dist[i] - dist[i+1] );
7163       else
7164         continue; // no intersection
7165       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7166       gp_Vec p2int ( point, pInt);
7167       if ( p2int * ray > -tol ) // right half-space
7168       {
7169         double intDist = p2int.SquareMagnitude();
7170         if ( intDist < distClosest )
7171         {
7172           iClosest = i;
7173           rClosest = r;
7174           pClosest = pInt;
7175           distClosest = intDist;
7176         }
7177       }
7178     }
7179     if ( iClosest < 0 )
7180       return true; // no intesections - out
7181
7182     // analyse transition
7183     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7184     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7185     gp_Vec p2int ( point, pClosest );
7186     bool out = (edgeNorm * p2int) < -tol;
7187     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7188       return out;
7189
7190     // ray pass through a face node; analyze transition through an adjacent edge
7191     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7192     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7193     gp_Vec edgeAdjacent( p1, p2 );
7194     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7195     bool out2 = (edgeNorm2 * p2int) < -tol;
7196
7197     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7198     return covexCorner ? (out || out2) : (out && out2);
7199   }
7200   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7201   {
7202     // point is out of edge if it is NOT ON any straight part of edge
7203     // (we consider quadratic edge as being composed of two straight parts)
7204     for ( i = 1; i < nbNodes; ++i )
7205     {
7206       gp_Vec edge( xyz[i-1], xyz[i]);
7207       gp_Vec n1p ( xyz[i-1], point);
7208       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7209       if ( dist > tol )
7210         continue;
7211       gp_Vec n2p( xyz[i], point );
7212       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7213         continue;
7214       return false; // point is ON this part
7215     }
7216     return true;
7217   }
7218   // Node or 0D element -------------------------------------------------------------------------
7219   {
7220     gp_Vec n2p ( xyz[0], point );
7221     return n2p.Magnitude() <= tol;
7222   }
7223   return true;
7224 }
7225
7226 //=======================================================================
7227
7228 namespace
7229 {
7230   // Position of a point relative to a segment
7231   //            .           .
7232   //            .  LEFT     .
7233   //            .           .
7234   //  VERTEX 1  o----ON----->  VERTEX 2
7235   //            .           .
7236   //            .  RIGHT    .
7237   //            .           .
7238   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7239                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7240   struct PointPos
7241   {
7242     PositionName _name; 
7243     int          _index; // index of vertex or segment
7244
7245     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7246     bool operator < (const PointPos& other ) const
7247     {
7248       if ( _name == other._name )
7249         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7250       return _name < other._name;
7251     }
7252   };
7253
7254   //================================================================================
7255   /*!
7256    * \brief Return of a point relative to a segment
7257    *  \param point2D      - the point to analyze position of
7258    *  \param xyVec        - end points of segments
7259    *  \param index0       - 0-based index of the first point of segment
7260    *  \param posToFindOut - flags of positions to detect
7261    *  \retval PointPos - point position
7262    */
7263   //================================================================================
7264
7265   PointPos getPointPosition( const gp_XY& point2D,
7266                              const gp_XY* segEnds,
7267                              const int    index0 = 0,
7268                              const int    posToFindOut = POS_ALL)
7269   {
7270     const gp_XY& p1 = segEnds[ index0   ];
7271     const gp_XY& p2 = segEnds[ index0+1 ];
7272     const gp_XY grad = p2 - p1;
7273
7274     if ( posToFindOut & POS_VERTEX )
7275     {
7276       // check if the point2D is at "vertex 1" zone
7277       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7278                                   p1.Y() + grad.X() ) };
7279       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7280         return PointPos( POS_VERTEX, index0 );
7281
7282       // check if the point2D is at "vertex 2" zone
7283       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7284                                   p2.Y() + grad.X() ) };
7285       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7286         return PointPos( POS_VERTEX, index0 + 1);
7287     }
7288     double edgeEquation =
7289       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7290     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7291   }
7292 }
7293
7294 //=======================================================================
7295 /*!
7296  * \brief Return minimal distance from a point to a face
7297  *
7298  * Currently we ignore non-planarity and 2nd order of face
7299  */
7300 //=======================================================================
7301
7302 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7303                                       const gp_Pnt&        point )
7304 {
7305   double badDistance = -1;
7306   if ( !face ) return badDistance;
7307
7308   // coordinates of nodes (medium nodes, if any, ignored)
7309   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7310   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7311   xyz.resize( face->NbCornerNodes()+1 );
7312
7313   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7314   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7315   gp_Trsf trsf;
7316   gp_Vec OZ ( xyz[0], xyz[1] );
7317   gp_Vec OX ( xyz[0], xyz[2] );
7318   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7319   {
7320     if ( xyz.size() < 4 ) return badDistance;
7321     OZ = gp_Vec ( xyz[0], xyz[2] );
7322     OX = gp_Vec ( xyz[0], xyz[3] );
7323   }
7324   gp_Ax3 tgtCS;
7325   try {
7326     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7327   }
7328   catch ( Standard_Failure ) {
7329     return badDistance;
7330   }
7331   trsf.SetTransformation( tgtCS );
7332
7333   // move all the nodes to 2D
7334   vector<gp_XY> xy( xyz.size() );
7335   for ( size_t i = 0;i < xyz.size()-1; ++i )
7336   {
7337     gp_XYZ p3d = xyz[i];
7338     trsf.Transforms( p3d );
7339     xy[i].SetCoord( p3d.X(), p3d.Z() );
7340   }
7341   xyz.back() = xyz.front();
7342   xy.back() = xy.front();
7343
7344   // // move the point in 2D
7345   gp_XYZ tmpPnt = point.XYZ();
7346   trsf.Transforms( tmpPnt );
7347   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7348
7349   // loop on segments of the face to analyze point position ralative to the face
7350   set< PointPos > pntPosSet;
7351   for ( size_t i = 1; i < xy.size(); ++i )
7352   {
7353     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7354     pntPosSet.insert( pos );
7355   }
7356
7357   // compute distance
7358   PointPos pos = *pntPosSet.begin();
7359   // cout << "Face " << face->GetID() << " DIST: ";
7360   switch ( pos._name )
7361   {
7362   case POS_LEFT: {
7363     // point is most close to a segment
7364     gp_Vec p0p1( point, xyz[ pos._index ] );
7365     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7366     p1p2.Normalize();
7367     double projDist = p0p1 * p1p2; // distance projected to the segment
7368     gp_Vec projVec = p1p2 * projDist;
7369     gp_Vec distVec = p0p1 - projVec;
7370     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7371     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7372     return distVec.Magnitude();
7373   }
7374   case POS_RIGHT: {
7375     // point is inside the face
7376     double distToFacePlane = tmpPnt.Y();
7377     // cout << distToFacePlane << ", INSIDE " << endl;
7378     return Abs( distToFacePlane );
7379   }
7380   case POS_VERTEX: {
7381     // point is most close to a node
7382     gp_Vec distVec( point, xyz[ pos._index ]);
7383     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7384     return distVec.Magnitude();
7385   }
7386   }
7387   return badDistance;
7388 }
7389
7390 //=======================================================================
7391 //function : SimplifyFace
7392 //purpose  :
7393 //=======================================================================
7394 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7395                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7396                                     vector<int>&                        quantities) const
7397 {
7398   int nbNodes = faceNodes.size();
7399
7400   if (nbNodes < 3)
7401     return 0;
7402
7403   set<const SMDS_MeshNode*> nodeSet;
7404
7405   // get simple seq of nodes
7406   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7407   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7408   int iSimple = 0, nbUnique = 0;
7409
7410   simpleNodes[iSimple++] = faceNodes[0];
7411   nbUnique++;
7412   for (int iCur = 1; iCur < nbNodes; iCur++) {
7413     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7414       simpleNodes[iSimple++] = faceNodes[iCur];
7415       if (nodeSet.insert( faceNodes[iCur] ).second)
7416         nbUnique++;
7417     }
7418   }
7419   int nbSimple = iSimple;
7420   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7421     nbSimple--;
7422     iSimple--;
7423   }
7424
7425   if (nbUnique < 3)
7426     return 0;
7427
7428   // separate loops
7429   int nbNew = 0;
7430   bool foundLoop = (nbSimple > nbUnique);
7431   while (foundLoop) {
7432     foundLoop = false;
7433     set<const SMDS_MeshNode*> loopSet;
7434     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7435       const SMDS_MeshNode* n = simpleNodes[iSimple];
7436       if (!loopSet.insert( n ).second) {
7437         foundLoop = true;
7438
7439         // separate loop
7440         int iC = 0, curLast = iSimple;
7441         for (; iC < curLast; iC++) {
7442           if (simpleNodes[iC] == n) break;
7443         }
7444         int loopLen = curLast - iC;
7445         if (loopLen > 2) {
7446           // create sub-element
7447           nbNew++;
7448           quantities.push_back(loopLen);
7449           for (; iC < curLast; iC++) {
7450             poly_nodes.push_back(simpleNodes[iC]);
7451           }
7452         }
7453         // shift the rest nodes (place from the first loop position)
7454         for (iC = curLast + 1; iC < nbSimple; iC++) {
7455           simpleNodes[iC - loopLen] = simpleNodes[iC];
7456         }
7457         nbSimple -= loopLen;
7458         iSimple -= loopLen;
7459       }
7460     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7461   } // while (foundLoop)
7462
7463   if (iSimple > 2) {
7464     nbNew++;
7465     quantities.push_back(iSimple);
7466     for (int i = 0; i < iSimple; i++)
7467       poly_nodes.push_back(simpleNodes[i]);
7468   }
7469
7470   return nbNew;
7471 }
7472
7473 //=======================================================================
7474 //function : MergeNodes
7475 //purpose  : In each group, the cdr of nodes are substituted by the first one
7476 //           in all elements.
7477 //=======================================================================
7478
7479 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7480 {
7481   MESSAGE("MergeNodes");
7482   myLastCreatedElems.Clear();
7483   myLastCreatedNodes.Clear();
7484
7485   SMESHDS_Mesh* aMesh = GetMeshDS();
7486
7487   TNodeNodeMap nodeNodeMap; // node to replace - new node
7488   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7489   list< int > rmElemIds, rmNodeIds;
7490
7491   // Fill nodeNodeMap and elems
7492
7493   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7494   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7495     list<const SMDS_MeshNode*>& nodes = *grIt;
7496     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7497     const SMDS_MeshNode* nToKeep = *nIt;
7498     //MESSAGE("node to keep " << nToKeep->GetID());
7499     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7500       const SMDS_MeshNode* nToRemove = *nIt;
7501       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7502       if ( nToRemove != nToKeep ) {
7503         //MESSAGE("  node to remove " << nToRemove->GetID());
7504         rmNodeIds.push_back( nToRemove->GetID() );
7505         AddToSameGroups( nToKeep, nToRemove, aMesh );
7506       }
7507
7508       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7509       while ( invElemIt->more() ) {
7510         const SMDS_MeshElement* elem = invElemIt->next();
7511         elems.insert(elem);
7512       }
7513     }
7514   }
7515   // Change element nodes or remove an element
7516
7517   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7518   for ( ; eIt != elems.end(); eIt++ ) {
7519     const SMDS_MeshElement* elem = *eIt;
7520     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7521     int nbNodes = elem->NbNodes();
7522     int aShapeId = FindShape( elem );
7523
7524     set<const SMDS_MeshNode*> nodeSet;
7525     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7526     int iUnique = 0, iCur = 0, nbRepl = 0;
7527     vector<int> iRepl( nbNodes );
7528
7529     // get new seq of nodes
7530     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7531     while ( itN->more() ) {
7532       const SMDS_MeshNode* n =
7533         static_cast<const SMDS_MeshNode*>( itN->next() );
7534
7535       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7536       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7537         n = (*nnIt).second;
7538         // BUG 0020185: begin
7539         {
7540           bool stopRecur = false;
7541           set<const SMDS_MeshNode*> nodesRecur;
7542           nodesRecur.insert(n);
7543           while (!stopRecur) {
7544             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7545             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7546               n = (*nnIt_i).second;
7547               if (!nodesRecur.insert(n).second) {
7548                 // error: recursive dependancy
7549                 stopRecur = true;
7550               }
7551             }
7552             else
7553               stopRecur = true;
7554           }
7555         }
7556         // BUG 0020185: end
7557       }
7558       curNodes[ iCur ] = n;
7559       bool isUnique = nodeSet.insert( n ).second;
7560       if ( isUnique )
7561         uniqueNodes[ iUnique++ ] = n;
7562       else
7563         iRepl[ nbRepl++ ] = iCur;
7564       iCur++;
7565     }
7566
7567     // Analyse element topology after replacement
7568
7569     bool isOk = true;
7570     int nbUniqueNodes = nodeSet.size();
7571     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7572     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7573       // Polygons and Polyhedral volumes
7574       if (elem->IsPoly()) {
7575
7576         if (elem->GetType() == SMDSAbs_Face) {
7577           // Polygon
7578           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7579           int inode = 0;
7580           for (; inode < nbNodes; inode++) {
7581             face_nodes[inode] = curNodes[inode];
7582           }
7583
7584           vector<const SMDS_MeshNode *> polygons_nodes;
7585           vector<int> quantities;
7586           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7587           if (nbNew > 0) {
7588             inode = 0;
7589             for (int iface = 0; iface < nbNew; iface++) {
7590               int nbNodes = quantities[iface];
7591               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7592               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7593                 poly_nodes[ii] = polygons_nodes[inode];
7594               }
7595               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7596               myLastCreatedElems.Append(newElem);
7597               if (aShapeId)
7598                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7599             }
7600
7601             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7602             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7603             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7604             int quid =0;
7605             if (nbNew > 0) quid = nbNew - 1;
7606             vector<int> newquant(quantities.begin()+quid, quantities.end());
7607             const SMDS_MeshElement* newElem = 0;
7608             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7609             myLastCreatedElems.Append(newElem);
7610             if ( aShapeId && newElem )
7611               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7612             rmElemIds.push_back(elem->GetID());
7613           }
7614           else {
7615             rmElemIds.push_back(elem->GetID());
7616           }
7617
7618         }
7619         else if (elem->GetType() == SMDSAbs_Volume) {
7620           // Polyhedral volume
7621           if (nbUniqueNodes < 4) {
7622             rmElemIds.push_back(elem->GetID());
7623           }
7624           else {
7625             // each face has to be analyzed in order to check volume validity
7626             const SMDS_VtkVolume* aPolyedre =
7627               dynamic_cast<const SMDS_VtkVolume*>( elem );
7628             if (aPolyedre) {
7629               int nbFaces = aPolyedre->NbFaces();
7630
7631               vector<const SMDS_MeshNode *> poly_nodes;
7632               vector<int> quantities;
7633
7634               for (int iface = 1; iface <= nbFaces; iface++) {
7635                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7636                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7637
7638                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7639                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7640                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7641                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7642                     faceNode = (*nnIt).second;
7643                   }
7644                   faceNodes[inode - 1] = faceNode;
7645                 }
7646
7647                 SimplifyFace(faceNodes, poly_nodes, quantities);
7648               }
7649
7650               if (quantities.size() > 3) {
7651                 // to be done: remove coincident faces
7652               }
7653
7654               if (quantities.size() > 3)
7655                 {
7656                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7657                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7658                   const SMDS_MeshElement* newElem = 0;
7659                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7660                   myLastCreatedElems.Append(newElem);
7661                   if ( aShapeId && newElem )
7662                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7663                   rmElemIds.push_back(elem->GetID());
7664                 }
7665             }
7666             else {
7667               rmElemIds.push_back(elem->GetID());
7668             }
7669           }
7670         }
7671         else {
7672         }
7673
7674         continue;
7675       } // poly element
7676
7677       // Regular elements
7678       // TODO not all the possible cases are solved. Find something more generic?
7679       switch ( nbNodes ) {
7680       case 2: ///////////////////////////////////// EDGE
7681         isOk = false; break;
7682       case 3: ///////////////////////////////////// TRIANGLE
7683         isOk = false; break;
7684       case 4:
7685         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7686           isOk = false;
7687         else { //////////////////////////////////// QUADRANGLE
7688           if ( nbUniqueNodes < 3 )
7689             isOk = false;
7690           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7691             isOk = false; // opposite nodes stick
7692           //MESSAGE("isOk " << isOk);
7693         }
7694         break;
7695       case 6: ///////////////////////////////////// PENTAHEDRON
7696         if ( nbUniqueNodes == 4 ) {
7697           // ---------------------------------> tetrahedron
7698           if (nbRepl == 3 &&
7699               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7700             // all top nodes stick: reverse a bottom
7701             uniqueNodes[ 0 ] = curNodes [ 1 ];
7702             uniqueNodes[ 1 ] = curNodes [ 0 ];
7703           }
7704           else if (nbRepl == 3 &&
7705                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7706             // all bottom nodes stick: set a top before
7707             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7708             uniqueNodes[ 0 ] = curNodes [ 3 ];
7709             uniqueNodes[ 1 ] = curNodes [ 4 ];
7710             uniqueNodes[ 2 ] = curNodes [ 5 ];
7711           }
7712           else if (nbRepl == 4 &&
7713                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7714             // a lateral face turns into a line: reverse a bottom
7715             uniqueNodes[ 0 ] = curNodes [ 1 ];
7716             uniqueNodes[ 1 ] = curNodes [ 0 ];
7717           }
7718           else
7719             isOk = false;
7720         }
7721         else if ( nbUniqueNodes == 5 ) {
7722           // PENTAHEDRON --------------------> 2 tetrahedrons
7723           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7724             // a bottom node sticks with a linked top one
7725             // 1.
7726             SMDS_MeshElement* newElem =
7727               aMesh->AddVolume(curNodes[ 3 ],
7728                                curNodes[ 4 ],
7729                                curNodes[ 5 ],
7730                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7731             myLastCreatedElems.Append(newElem);
7732             if ( aShapeId )
7733               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7734             // 2. : reverse a bottom
7735             uniqueNodes[ 0 ] = curNodes [ 1 ];
7736             uniqueNodes[ 1 ] = curNodes [ 0 ];
7737             nbUniqueNodes = 4;
7738           }
7739           else
7740             isOk = false;
7741         }
7742         else
7743           isOk = false;
7744         break;
7745       case 8: {
7746         if(elem->IsQuadratic()) { // Quadratic quadrangle
7747           //   1    5    2
7748           //    +---+---+
7749           //    |       |
7750           //    |       |
7751           //   4+       +6
7752           //    |       |
7753           //    |       |
7754           //    +---+---+
7755           //   0    7    3
7756           isOk = false;
7757           if(nbRepl==2) {
7758             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7759           }
7760           if(nbRepl==3) {
7761             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7762             nbUniqueNodes = 6;
7763             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7764               uniqueNodes[0] = curNodes[0];
7765               uniqueNodes[1] = curNodes[2];
7766               uniqueNodes[2] = curNodes[3];
7767               uniqueNodes[3] = curNodes[5];
7768               uniqueNodes[4] = curNodes[6];
7769               uniqueNodes[5] = curNodes[7];
7770               isOk = true;
7771             }
7772             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7773               uniqueNodes[0] = curNodes[0];
7774               uniqueNodes[1] = curNodes[1];
7775               uniqueNodes[2] = curNodes[2];
7776               uniqueNodes[3] = curNodes[4];
7777               uniqueNodes[4] = curNodes[5];
7778               uniqueNodes[5] = curNodes[6];
7779               isOk = true;
7780             }
7781             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7782               uniqueNodes[0] = curNodes[1];
7783               uniqueNodes[1] = curNodes[2];
7784               uniqueNodes[2] = curNodes[3];
7785               uniqueNodes[3] = curNodes[5];
7786               uniqueNodes[4] = curNodes[6];
7787               uniqueNodes[5] = curNodes[0];
7788               isOk = true;
7789             }
7790             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7791               uniqueNodes[0] = curNodes[0];
7792               uniqueNodes[1] = curNodes[1];
7793               uniqueNodes[2] = curNodes[3];
7794               uniqueNodes[3] = curNodes[4];
7795               uniqueNodes[4] = curNodes[6];
7796               uniqueNodes[5] = curNodes[7];
7797               isOk = true;
7798             }
7799             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7800               uniqueNodes[0] = curNodes[0];
7801               uniqueNodes[1] = curNodes[2];
7802               uniqueNodes[2] = curNodes[3];
7803               uniqueNodes[3] = curNodes[1];
7804               uniqueNodes[4] = curNodes[6];
7805               uniqueNodes[5] = curNodes[7];
7806               isOk = true;
7807             }
7808             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7809               uniqueNodes[0] = curNodes[0];
7810               uniqueNodes[1] = curNodes[1];
7811               uniqueNodes[2] = curNodes[2];
7812               uniqueNodes[3] = curNodes[4];
7813               uniqueNodes[4] = curNodes[5];
7814               uniqueNodes[5] = curNodes[7];
7815               isOk = true;
7816             }
7817             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7818               uniqueNodes[0] = curNodes[0];
7819               uniqueNodes[1] = curNodes[1];
7820               uniqueNodes[2] = curNodes[3];
7821               uniqueNodes[3] = curNodes[4];
7822               uniqueNodes[4] = curNodes[2];
7823               uniqueNodes[5] = curNodes[7];
7824               isOk = true;
7825             }
7826             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7827               uniqueNodes[0] = curNodes[0];
7828               uniqueNodes[1] = curNodes[1];
7829               uniqueNodes[2] = curNodes[2];
7830               uniqueNodes[3] = curNodes[4];
7831               uniqueNodes[4] = curNodes[5];
7832               uniqueNodes[5] = curNodes[3];
7833               isOk = true;
7834             }
7835           }
7836           if(nbRepl==4) {
7837             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7838           }
7839           if(nbRepl==5) {
7840             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7841           }
7842           break;
7843         }
7844         //////////////////////////////////// HEXAHEDRON
7845         isOk = false;
7846         SMDS_VolumeTool hexa (elem);
7847         hexa.SetExternalNormal();
7848         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7849           //////////////////////// HEX ---> 1 tetrahedron
7850           for ( int iFace = 0; iFace < 6; iFace++ ) {
7851             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7852             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7853                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7854                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7855               // one face turns into a point ...
7856               int iOppFace = hexa.GetOppFaceIndex( iFace );
7857               ind = hexa.GetFaceNodesIndices( iOppFace );
7858               int nbStick = 0;
7859               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7860                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7861                   nbStick++;
7862               }
7863               if ( nbStick == 1 ) {
7864                 // ... and the opposite one - into a triangle.
7865                 // set a top node
7866                 ind = hexa.GetFaceNodesIndices( iFace );
7867                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7868                 isOk = true;
7869               }
7870               break;
7871             }
7872           }
7873         }
7874         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7875           //////////////////////// HEX ---> 1 prism
7876           int nbTria = 0, iTria[3];
7877           const int *ind; // indices of face nodes
7878           // look for triangular faces
7879           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7880             ind = hexa.GetFaceNodesIndices( iFace );
7881             TIDSortedNodeSet faceNodes;
7882             for ( iCur = 0; iCur < 4; iCur++ )
7883               faceNodes.insert( curNodes[ind[iCur]] );
7884             if ( faceNodes.size() == 3 )
7885               iTria[ nbTria++ ] = iFace;
7886           }
7887           // check if triangles are opposite
7888           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7889           {
7890             isOk = true;
7891             // set nodes of the bottom triangle
7892             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7893             vector<int> indB;
7894             for ( iCur = 0; iCur < 4; iCur++ )
7895               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7896                 indB.push_back( ind[iCur] );
7897             if ( !hexa.IsForward() )
7898               std::swap( indB[0], indB[2] );
7899             for ( iCur = 0; iCur < 3; iCur++ )
7900               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7901             // set nodes of the top triangle
7902             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7903             for ( iCur = 0; iCur < 3; ++iCur )
7904               for ( int j = 0; j < 4; ++j )
7905                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7906                 {
7907                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7908                   break;
7909                 }
7910           }
7911           break;
7912         }
7913         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7914           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7915           for ( int iFace = 0; iFace < 6; iFace++ ) {
7916             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7917             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7918                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7919                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7920               // one face turns into a point ...
7921               int iOppFace = hexa.GetOppFaceIndex( iFace );
7922               ind = hexa.GetFaceNodesIndices( iOppFace );
7923               int nbStick = 0;
7924               iUnique = 2;  // reverse a tetrahedron 1 bottom
7925               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7926                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7927                   nbStick++;
7928                 else if ( iUnique >= 0 )
7929                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7930               }
7931               if ( nbStick == 0 ) {
7932                 // ... and the opposite one is a quadrangle
7933                 // set a top node
7934                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7935                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7936                 nbUniqueNodes = 4;
7937                 // tetrahedron 2
7938                 SMDS_MeshElement* newElem =
7939                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7940                                    curNodes[ind[ 3 ]],
7941                                    curNodes[ind[ 2 ]],
7942                                    curNodes[indTop[ 0 ]]);
7943                 myLastCreatedElems.Append(newElem);
7944                 if ( aShapeId )
7945                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7946                 isOk = true;
7947               }
7948               break;
7949             }
7950           }
7951         }
7952         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7953           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7954           // find indices of quad and tri faces
7955           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7956           for ( iFace = 0; iFace < 6; iFace++ ) {
7957             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7958             nodeSet.clear();
7959             for ( iCur = 0; iCur < 4; iCur++ )
7960               nodeSet.insert( curNodes[ind[ iCur ]] );
7961             nbUniqueNodes = nodeSet.size();
7962             if ( nbUniqueNodes == 3 )
7963               iTriFace[ nbTri++ ] = iFace;
7964             else if ( nbUniqueNodes == 4 )
7965               iQuadFace[ nbQuad++ ] = iFace;
7966           }
7967           if (nbQuad == 2 && nbTri == 4 &&
7968               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7969             // 2 opposite quadrangles stuck with a diagonal;
7970             // sample groups of merged indices: (0-4)(2-6)
7971             // --------------------------------------------> 2 tetrahedrons
7972             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7973             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7974             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7975             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7976                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7977               // stuck with 0-2 diagonal
7978               i0  = ind1[ 3 ];
7979               i1d = ind1[ 0 ];
7980               i2  = ind1[ 1 ];
7981               i3d = ind1[ 2 ];
7982               i0t = ind2[ 1 ];
7983               i2t = ind2[ 3 ];
7984             }
7985             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7986                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7987               // stuck with 1-3 diagonal
7988               i0  = ind1[ 0 ];
7989               i1d = ind1[ 1 ];
7990               i2  = ind1[ 2 ];
7991               i3d = ind1[ 3 ];
7992               i0t = ind2[ 0 ];
7993               i2t = ind2[ 1 ];
7994             }
7995             else {
7996               ASSERT(0);
7997             }
7998             // tetrahedron 1
7999             uniqueNodes[ 0 ] = curNodes [ i0 ];
8000             uniqueNodes[ 1 ] = curNodes [ i1d ];
8001             uniqueNodes[ 2 ] = curNodes [ i3d ];
8002             uniqueNodes[ 3 ] = curNodes [ i0t ];
8003             nbUniqueNodes = 4;
8004             // tetrahedron 2
8005             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8006                                                          curNodes[ i2 ],
8007                                                          curNodes[ i3d ],
8008                                                          curNodes[ i2t ]);
8009             myLastCreatedElems.Append(newElem);
8010             if ( aShapeId )
8011               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8012             isOk = true;
8013           }
8014           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8015                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8016             // --------------------------------------------> prism
8017             // find 2 opposite triangles
8018             nbUniqueNodes = 6;
8019             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8020               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8021                 // find indices of kept and replaced nodes
8022                 // and fill unique nodes of 2 opposite triangles
8023                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8024                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8025                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8026                 // fill unique nodes
8027                 iUnique = 0;
8028                 isOk = true;
8029                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8030                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8031                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8032                   if ( n == nInit ) {
8033                     // iCur of a linked node of the opposite face (make normals co-directed):
8034                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8035                     // check that correspondent corners of triangles are linked
8036                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8037                       isOk = false;
8038                     else {
8039                       uniqueNodes[ iUnique ] = n;
8040                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8041                       iUnique++;
8042                     }
8043                   }
8044                 }
8045                 break;
8046               }
8047             }
8048           }
8049         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8050         else
8051         {
8052           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8053         }
8054         break;
8055       } // HEXAHEDRON
8056
8057       default:
8058         isOk = false;
8059       } // switch ( nbNodes )
8060
8061     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8062
8063     if ( isOk ) { // the elem remains valid after sticking nodes
8064       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8065       {
8066         // Change nodes of polyedre
8067         const SMDS_VtkVolume* aPolyedre =
8068           dynamic_cast<const SMDS_VtkVolume*>( elem );
8069         if (aPolyedre) {
8070           int nbFaces = aPolyedre->NbFaces();
8071
8072           vector<const SMDS_MeshNode *> poly_nodes;
8073           vector<int> quantities (nbFaces);
8074
8075           for (int iface = 1; iface <= nbFaces; iface++) {
8076             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8077             quantities[iface - 1] = nbFaceNodes;
8078
8079             for (inode = 1; inode <= nbFaceNodes; inode++) {
8080               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8081
8082               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8083               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8084                 curNode = (*nnIt).second;
8085               }
8086               poly_nodes.push_back(curNode);
8087             }
8088           }
8089           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8090         }
8091       }
8092       else // replace non-polyhedron elements
8093       {
8094         const SMDSAbs_ElementType etyp = elem->GetType();
8095         const int elemId               = elem->GetID();
8096         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8097         uniqueNodes.resize(nbUniqueNodes);
8098
8099         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8100
8101         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8102         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8103         if ( sm && newElem )
8104           sm->AddElement( newElem );
8105         if ( elem != newElem )
8106           ReplaceElemInGroups( elem, newElem, aMesh );
8107       }
8108     }
8109     else {
8110       // Remove invalid regular element or invalid polygon
8111       rmElemIds.push_back( elem->GetID() );
8112     }
8113
8114   } // loop on elements
8115
8116   // Remove bad elements, then equal nodes (order important)
8117
8118   Remove( rmElemIds, false );
8119   Remove( rmNodeIds, true );
8120
8121 }
8122
8123
8124 // ========================================================
8125 // class   : SortableElement
8126 // purpose : allow sorting elements basing on their nodes
8127 // ========================================================
8128 class SortableElement : public set <const SMDS_MeshElement*>
8129 {
8130 public:
8131
8132   SortableElement( const SMDS_MeshElement* theElem )
8133   {
8134     myElem = theElem;
8135     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8136     while ( nodeIt->more() )
8137       this->insert( nodeIt->next() );
8138   }
8139
8140   const SMDS_MeshElement* Get() const
8141   { return myElem; }
8142
8143   void Set(const SMDS_MeshElement* e) const
8144   { myElem = e; }
8145
8146
8147 private:
8148   mutable const SMDS_MeshElement* myElem;
8149 };
8150
8151 //=======================================================================
8152 //function : FindEqualElements
8153 //purpose  : Return list of group of elements built on the same nodes.
8154 //           Search among theElements or in the whole mesh if theElements is empty
8155 //=======================================================================
8156 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
8157                                          TListOfListOfElementsID &      theGroupsOfElementsID)
8158 {
8159   myLastCreatedElems.Clear();
8160   myLastCreatedNodes.Clear();
8161
8162   typedef set<const SMDS_MeshElement*> TElemsSet;
8163   typedef map< SortableElement, int > TMapOfNodeSet;
8164   typedef list<int> TGroupOfElems;
8165
8166   TElemsSet elems;
8167   if ( theElements.empty() )
8168   { // get all elements in the mesh
8169     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8170     while ( eIt->more() )
8171       elems.insert( elems.end(), eIt->next());
8172   }
8173   else
8174     elems = theElements;
8175
8176   vector< TGroupOfElems > arrayOfGroups;
8177   TGroupOfElems groupOfElems;
8178   TMapOfNodeSet mapOfNodeSet;
8179
8180   TElemsSet::iterator elemIt = elems.begin();
8181   for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
8182     const SMDS_MeshElement* curElem = *elemIt;
8183     SortableElement SE(curElem);
8184     int ind = -1;
8185     // check uniqueness
8186     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8187     if( !(pp.second) ) {
8188       TMapOfNodeSet::iterator& itSE = pp.first;
8189       ind = (*itSE).second;
8190       arrayOfGroups[ind].push_back(curElem->GetID());
8191     }
8192     else {
8193       groupOfElems.clear();
8194       groupOfElems.push_back(curElem->GetID());
8195       arrayOfGroups.push_back(groupOfElems);
8196       i++;
8197     }
8198   }
8199
8200   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8201   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8202     groupOfElems = *groupIt;
8203     if ( groupOfElems.size() > 1 ) {
8204       groupOfElems.sort();
8205       theGroupsOfElementsID.push_back(groupOfElems);
8206     }
8207   }
8208 }
8209
8210 //=======================================================================
8211 //function : MergeElements
8212 //purpose  : In each given group, substitute all elements by the first one.
8213 //=======================================================================
8214
8215 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8216 {
8217   myLastCreatedElems.Clear();
8218   myLastCreatedNodes.Clear();
8219
8220   typedef list<int> TListOfIDs;
8221   TListOfIDs rmElemIds; // IDs of elems to remove
8222
8223   SMESHDS_Mesh* aMesh = GetMeshDS();
8224
8225   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8226   while ( groupsIt != theGroupsOfElementsID.end() ) {
8227     TListOfIDs& aGroupOfElemID = *groupsIt;
8228     aGroupOfElemID.sort();
8229     int elemIDToKeep = aGroupOfElemID.front();
8230     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8231     aGroupOfElemID.pop_front();
8232     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8233     while ( idIt != aGroupOfElemID.end() ) {
8234       int elemIDToRemove = *idIt;
8235       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8236       // add the kept element in groups of removed one (PAL15188)
8237       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8238       rmElemIds.push_back( elemIDToRemove );
8239       ++idIt;
8240     }
8241     ++groupsIt;
8242   }
8243
8244   Remove( rmElemIds, false );
8245 }
8246
8247 //=======================================================================
8248 //function : MergeEqualElements
8249 //purpose  : Remove all but one of elements built on the same nodes.
8250 //=======================================================================
8251
8252 void SMESH_MeshEditor::MergeEqualElements()
8253 {
8254   set<const SMDS_MeshElement*> aMeshElements; /* empty input -
8255                                                  to merge equal elements in the whole mesh */
8256   TListOfListOfElementsID aGroupsOfElementsID;
8257   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8258   MergeElements(aGroupsOfElementsID);
8259 }
8260
8261 //=======================================================================
8262 //function : FindFaceInSet
8263 //purpose  : Return a face having linked nodes n1 and n2 and which is
8264 //           - not in avoidSet,
8265 //           - in elemSet provided that !elemSet.empty()
8266 //           i1 and i2 optionally returns indices of n1 and n2
8267 //=======================================================================
8268
8269 const SMDS_MeshElement*
8270 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8271                                 const SMDS_MeshNode*    n2,
8272                                 const TIDSortedElemSet& elemSet,
8273                                 const TIDSortedElemSet& avoidSet,
8274                                 int*                    n1ind,
8275                                 int*                    n2ind)
8276
8277 {
8278   int i1, i2;
8279   const SMDS_MeshElement* face = 0;
8280
8281   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8282   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8283   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8284   {
8285     //MESSAGE("in while ( invElemIt->more() && !face )");
8286     const SMDS_MeshElement* elem = invElemIt->next();
8287     if (avoidSet.count( elem ))
8288       continue;
8289     if ( !elemSet.empty() && !elemSet.count( elem ))
8290       continue;
8291     // index of n1
8292     i1 = elem->GetNodeIndex( n1 );
8293     // find a n2 linked to n1
8294     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8295     for ( int di = -1; di < 2 && !face; di += 2 )
8296     {
8297       i2 = (i1+di+nbN) % nbN;
8298       if ( elem->GetNode( i2 ) == n2 )
8299         face = elem;
8300     }
8301     if ( !face && elem->IsQuadratic())
8302     {
8303       // analysis for quadratic elements using all nodes
8304       const SMDS_VtkFace* F =
8305         dynamic_cast<const SMDS_VtkFace*>(elem);
8306       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8307       // use special nodes iterator
8308       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8309       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8310       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8311       {
8312         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8313         if ( n1 == prevN && n2 == n )
8314         {
8315           face = elem;
8316         }
8317         else if ( n2 == prevN && n1 == n )
8318         {
8319           face = elem; swap( i1, i2 );
8320         }
8321         prevN = n;
8322       }
8323     }
8324   }
8325   if ( n1ind ) *n1ind = i1;
8326   if ( n2ind ) *n2ind = i2;
8327   return face;
8328 }
8329
8330 //=======================================================================
8331 //function : findAdjacentFace
8332 //purpose  :
8333 //=======================================================================
8334
8335 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8336                                                 const SMDS_MeshNode* n2,
8337                                                 const SMDS_MeshElement* elem)
8338 {
8339   TIDSortedElemSet elemSet, avoidSet;
8340   if ( elem )
8341     avoidSet.insert ( elem );
8342   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8343 }
8344
8345 //=======================================================================
8346 //function : FindFreeBorder
8347 //purpose  :
8348 //=======================================================================
8349
8350 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8351
8352 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8353                                        const SMDS_MeshNode*             theSecondNode,
8354                                        const SMDS_MeshNode*             theLastNode,
8355                                        list< const SMDS_MeshNode* > &   theNodes,
8356                                        list< const SMDS_MeshElement* >& theFaces)
8357 {
8358   if ( !theFirstNode || !theSecondNode )
8359     return false;
8360   // find border face between theFirstNode and theSecondNode
8361   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8362   if ( !curElem )
8363     return false;
8364
8365   theFaces.push_back( curElem );
8366   theNodes.push_back( theFirstNode );
8367   theNodes.push_back( theSecondNode );
8368
8369   //vector<const SMDS_MeshNode*> nodes;
8370   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8371   TIDSortedElemSet foundElems;
8372   bool needTheLast = ( theLastNode != 0 );
8373
8374   while ( nStart != theLastNode ) {
8375     if ( nStart == theFirstNode )
8376       return !needTheLast;
8377
8378     // find all free border faces sharing form nStart
8379
8380     list< const SMDS_MeshElement* > curElemList;
8381     list< const SMDS_MeshNode* > nStartList;
8382     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8383     while ( invElemIt->more() ) {
8384       const SMDS_MeshElement* e = invElemIt->next();
8385       if ( e == curElem || foundElems.insert( e ).second ) {
8386         // get nodes
8387         int iNode = 0, nbNodes = e->NbNodes();
8388         //const SMDS_MeshNode* nodes[nbNodes+1];
8389         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8390
8391         if(e->IsQuadratic()) {
8392           const SMDS_VtkFace* F =
8393             dynamic_cast<const SMDS_VtkFace*>(e);
8394           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8395           // use special nodes iterator
8396           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8397           while( anIter->more() ) {
8398             nodes[ iNode++ ] = cast2Node(anIter->next());
8399           }
8400         }
8401         else {
8402           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8403           while ( nIt->more() )
8404             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8405         }
8406         nodes[ iNode ] = nodes[ 0 ];
8407         // check 2 links
8408         for ( iNode = 0; iNode < nbNodes; iNode++ )
8409           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8410                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8411               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8412           {
8413             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8414             curElemList.push_back( e );
8415           }
8416       }
8417     }
8418     // analyse the found
8419
8420     int nbNewBorders = curElemList.size();
8421     if ( nbNewBorders == 0 ) {
8422       // no free border furthermore
8423       return !needTheLast;
8424     }
8425     else if ( nbNewBorders == 1 ) {
8426       // one more element found
8427       nIgnore = nStart;
8428       nStart = nStartList.front();
8429       curElem = curElemList.front();
8430       theFaces.push_back( curElem );
8431       theNodes.push_back( nStart );
8432     }
8433     else {
8434       // several continuations found
8435       list< const SMDS_MeshElement* >::iterator curElemIt;
8436       list< const SMDS_MeshNode* >::iterator nStartIt;
8437       // check if one of them reached the last node
8438       if ( needTheLast ) {
8439         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8440              curElemIt!= curElemList.end();
8441              curElemIt++, nStartIt++ )
8442           if ( *nStartIt == theLastNode ) {
8443             theFaces.push_back( *curElemIt );
8444             theNodes.push_back( *nStartIt );
8445             return true;
8446           }
8447       }
8448       // find the best free border by the continuations
8449       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8450       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8451       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8452            curElemIt!= curElemList.end();
8453            curElemIt++, nStartIt++ )
8454       {
8455         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8456         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8457         // find one more free border
8458         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8459           cNL->clear();
8460           cFL->clear();
8461         }
8462         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8463           // choice: clear a worse one
8464           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8465           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8466           contNodes[ iWorse ].clear();
8467           contFaces[ iWorse ].clear();
8468         }
8469       }
8470       if ( contNodes[0].empty() && contNodes[1].empty() )
8471         return false;
8472
8473       // append the best free border
8474       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8475       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8476       theNodes.pop_back(); // remove nIgnore
8477       theNodes.pop_back(); // remove nStart
8478       theFaces.pop_back(); // remove curElem
8479       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8480       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8481       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8482       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8483       return true;
8484
8485     } // several continuations found
8486   } // while ( nStart != theLastNode )
8487
8488   return true;
8489 }
8490
8491 //=======================================================================
8492 //function : CheckFreeBorderNodes
8493 //purpose  : Return true if the tree nodes are on a free border
8494 //=======================================================================
8495
8496 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8497                                             const SMDS_MeshNode* theNode2,
8498                                             const SMDS_MeshNode* theNode3)
8499 {
8500   list< const SMDS_MeshNode* > nodes;
8501   list< const SMDS_MeshElement* > faces;
8502   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8503 }
8504
8505 //=======================================================================
8506 //function : SewFreeBorder
8507 //purpose  :
8508 //=======================================================================
8509
8510 SMESH_MeshEditor::Sew_Error
8511 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8512                                  const SMDS_MeshNode* theBordSecondNode,
8513                                  const SMDS_MeshNode* theBordLastNode,
8514                                  const SMDS_MeshNode* theSideFirstNode,
8515                                  const SMDS_MeshNode* theSideSecondNode,
8516                                  const SMDS_MeshNode* theSideThirdNode,
8517                                  const bool           theSideIsFreeBorder,
8518                                  const bool           toCreatePolygons,
8519                                  const bool           toCreatePolyedrs)
8520 {
8521   myLastCreatedElems.Clear();
8522   myLastCreatedNodes.Clear();
8523
8524   MESSAGE("::SewFreeBorder()");
8525   Sew_Error aResult = SEW_OK;
8526
8527   // ====================================
8528   //    find side nodes and elements
8529   // ====================================
8530
8531   list< const SMDS_MeshNode* > nSide[ 2 ];
8532   list< const SMDS_MeshElement* > eSide[ 2 ];
8533   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8534   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8535
8536   // Free border 1
8537   // --------------
8538   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8539                       nSide[0], eSide[0])) {
8540     MESSAGE(" Free Border 1 not found " );
8541     aResult = SEW_BORDER1_NOT_FOUND;
8542   }
8543   if (theSideIsFreeBorder) {
8544     // Free border 2
8545     // --------------
8546     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8547                         nSide[1], eSide[1])) {
8548       MESSAGE(" Free Border 2 not found " );
8549       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8550     }
8551   }
8552   if ( aResult != SEW_OK )
8553     return aResult;
8554
8555   if (!theSideIsFreeBorder) {
8556     // Side 2
8557     // --------------
8558
8559     // -------------------------------------------------------------------------
8560     // Algo:
8561     // 1. If nodes to merge are not coincident, move nodes of the free border
8562     //    from the coord sys defined by the direction from the first to last
8563     //    nodes of the border to the correspondent sys of the side 2
8564     // 2. On the side 2, find the links most co-directed with the correspondent
8565     //    links of the free border
8566     // -------------------------------------------------------------------------
8567
8568     // 1. Since sewing may break if there are volumes to split on the side 2,
8569     //    we wont move nodes but just compute new coordinates for them
8570     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8571     TNodeXYZMap nBordXYZ;
8572     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8573     list< const SMDS_MeshNode* >::iterator nBordIt;
8574
8575     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8576     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8577     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8578     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8579     double tol2 = 1.e-8;
8580     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8581     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8582       // Need node movement.
8583
8584       // find X and Z axes to create trsf
8585       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8586       gp_Vec X = Zs ^ Zb;
8587       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8588         // Zb || Zs
8589         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8590
8591       // coord systems
8592       gp_Ax3 toBordAx( Pb1, Zb, X );
8593       gp_Ax3 fromSideAx( Ps1, Zs, X );
8594       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8595       // set trsf
8596       gp_Trsf toBordSys, fromSide2Sys;
8597       toBordSys.SetTransformation( toBordAx );
8598       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8599       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8600
8601       // move
8602       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8603         const SMDS_MeshNode* n = *nBordIt;
8604         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8605         toBordSys.Transforms( xyz );
8606         fromSide2Sys.Transforms( xyz );
8607         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8608       }
8609     }
8610     else {
8611       // just insert nodes XYZ in the nBordXYZ map
8612       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8613         const SMDS_MeshNode* n = *nBordIt;
8614         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8615       }
8616     }
8617
8618     // 2. On the side 2, find the links most co-directed with the correspondent
8619     //    links of the free border
8620
8621     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8622     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8623     sideNodes.push_back( theSideFirstNode );
8624
8625     bool hasVolumes = false;
8626     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8627     set<long> foundSideLinkIDs, checkedLinkIDs;
8628     SMDS_VolumeTool volume;
8629     //const SMDS_MeshNode* faceNodes[ 4 ];
8630
8631     const SMDS_MeshNode*    sideNode;
8632     const SMDS_MeshElement* sideElem;
8633     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8634     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8635     nBordIt = bordNodes.begin();
8636     nBordIt++;
8637     // border node position and border link direction to compare with
8638     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8639     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8640     // choose next side node by link direction or by closeness to
8641     // the current border node:
8642     bool searchByDir = ( *nBordIt != theBordLastNode );
8643     do {
8644       // find the next node on the Side 2
8645       sideNode = 0;
8646       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8647       long linkID;
8648       checkedLinkIDs.clear();
8649       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8650
8651       // loop on inverse elements of current node (prevSideNode) on the Side 2
8652       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8653       while ( invElemIt->more() )
8654       {
8655         const SMDS_MeshElement* elem = invElemIt->next();
8656         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8657         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8658         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8659         bool isVolume = volume.Set( elem );
8660         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8661         if ( isVolume ) // --volume
8662           hasVolumes = true;
8663         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8664           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8665           if(elem->IsQuadratic()) {
8666             const SMDS_VtkFace* F =
8667               dynamic_cast<const SMDS_VtkFace*>(elem);
8668             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8669             // use special nodes iterator
8670             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8671             while( anIter->more() ) {
8672               nodes[ iNode ] = cast2Node(anIter->next());
8673               if ( nodes[ iNode++ ] == prevSideNode )
8674                 iPrevNode = iNode - 1;
8675             }
8676           }
8677           else {
8678             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8679             while ( nIt->more() ) {
8680               nodes[ iNode ] = cast2Node( nIt->next() );
8681               if ( nodes[ iNode++ ] == prevSideNode )
8682                 iPrevNode = iNode - 1;
8683             }
8684           }
8685           // there are 2 links to check
8686           nbNodes = 2;
8687         }
8688         else // --edge
8689           continue;
8690         // loop on links, to be precise, on the second node of links
8691         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8692           const SMDS_MeshNode* n = nodes[ iNode ];
8693           if ( isVolume ) {
8694             if ( !volume.IsLinked( n, prevSideNode ))
8695               continue;
8696           }
8697           else {
8698             if ( iNode ) // a node before prevSideNode
8699               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8700             else         // a node after prevSideNode
8701               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8702           }
8703           // check if this link was already used
8704           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8705           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8706           if (!isJustChecked &&
8707               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8708           {
8709             // test a link geometrically
8710             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8711             bool linkIsBetter = false;
8712             double dot = 0.0, dist = 0.0;
8713             if ( searchByDir ) { // choose most co-directed link
8714               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8715               linkIsBetter = ( dot > maxDot );
8716             }
8717             else { // choose link with the node closest to bordPos
8718               dist = ( nextXYZ - bordPos ).SquareModulus();
8719               linkIsBetter = ( dist < minDist );
8720             }
8721             if ( linkIsBetter ) {
8722               maxDot = dot;
8723               minDist = dist;
8724               linkID = iLink;
8725               sideNode = n;
8726               sideElem = elem;
8727             }
8728           }
8729         }
8730       } // loop on inverse elements of prevSideNode
8731
8732       if ( !sideNode ) {
8733         MESSAGE(" Cant find path by links of the Side 2 ");
8734         return SEW_BAD_SIDE_NODES;
8735       }
8736       sideNodes.push_back( sideNode );
8737       sideElems.push_back( sideElem );
8738       foundSideLinkIDs.insert ( linkID );
8739       prevSideNode = sideNode;
8740
8741       if ( *nBordIt == theBordLastNode )
8742         searchByDir = false;
8743       else {
8744         // find the next border link to compare with
8745         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8746         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8747         // move to next border node if sideNode is before forward border node (bordPos)
8748         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8749           prevBordNode = *nBordIt;
8750           nBordIt++;
8751           bordPos = nBordXYZ[ *nBordIt ];
8752           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8753           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8754         }
8755       }
8756     }
8757     while ( sideNode != theSideSecondNode );
8758
8759     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8760       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8761       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8762     }
8763   } // end nodes search on the side 2
8764
8765   // ============================
8766   // sew the border to the side 2
8767   // ============================
8768
8769   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8770   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8771
8772   TListOfListOfNodes nodeGroupsToMerge;
8773   if ( nbNodes[0] == nbNodes[1] ||
8774        ( theSideIsFreeBorder && !theSideThirdNode)) {
8775
8776     // all nodes are to be merged
8777
8778     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8779          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8780          nIt[0]++, nIt[1]++ )
8781     {
8782       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8783       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8784       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8785     }
8786   }
8787   else {
8788
8789     // insert new nodes into the border and the side to get equal nb of segments
8790
8791     // get normalized parameters of nodes on the borders
8792     //double param[ 2 ][ maxNbNodes ];
8793     double* param[ 2 ];
8794     param[0] = new double [ maxNbNodes ];
8795     param[1] = new double [ maxNbNodes ];
8796     int iNode, iBord;
8797     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8798       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8799       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8800       const SMDS_MeshNode* nPrev = *nIt;
8801       double bordLength = 0;
8802       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8803         const SMDS_MeshNode* nCur = *nIt;
8804         gp_XYZ segment (nCur->X() - nPrev->X(),
8805                         nCur->Y() - nPrev->Y(),
8806                         nCur->Z() - nPrev->Z());
8807         double segmentLen = segment.Modulus();
8808         bordLength += segmentLen;
8809         param[ iBord ][ iNode ] = bordLength;
8810         nPrev = nCur;
8811       }
8812       // normalize within [0,1]
8813       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8814         param[ iBord ][ iNode ] /= bordLength;
8815       }
8816     }
8817
8818     // loop on border segments
8819     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8820     int i[ 2 ] = { 0, 0 };
8821     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8822     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8823
8824     TElemOfNodeListMap insertMap;
8825     TElemOfNodeListMap::iterator insertMapIt;
8826     // insertMap is
8827     // key:   elem to insert nodes into
8828     // value: 2 nodes to insert between + nodes to be inserted
8829     do {
8830       bool next[ 2 ] = { false, false };
8831
8832       // find min adjacent segment length after sewing
8833       double nextParam = 10., prevParam = 0;
8834       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8835         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8836           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8837         if ( i[ iBord ] > 0 )
8838           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8839       }
8840       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8841       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8842       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8843
8844       // choose to insert or to merge nodes
8845       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8846       if ( Abs( du ) <= minSegLen * 0.2 ) {
8847         // merge
8848         // ------
8849         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8850         const SMDS_MeshNode* n0 = *nIt[0];
8851         const SMDS_MeshNode* n1 = *nIt[1];
8852         nodeGroupsToMerge.back().push_back( n1 );
8853         nodeGroupsToMerge.back().push_back( n0 );
8854         // position of node of the border changes due to merge
8855         param[ 0 ][ i[0] ] += du;
8856         // move n1 for the sake of elem shape evaluation during insertion.
8857         // n1 will be removed by MergeNodes() anyway
8858         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8859         next[0] = next[1] = true;
8860       }
8861       else {
8862         // insert
8863         // ------
8864         int intoBord = ( du < 0 ) ? 0 : 1;
8865         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8866         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8867         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8868         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8869         if ( intoBord == 1 ) {
8870           // move node of the border to be on a link of elem of the side
8871           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8872           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8873           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8874           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8875           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8876         }
8877         insertMapIt = insertMap.find( elem );
8878         bool notFound = ( insertMapIt == insertMap.end() );
8879         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8880         if ( otherLink ) {
8881           // insert into another link of the same element:
8882           // 1. perform insertion into the other link of the elem
8883           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8884           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8885           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8886           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8887           // 2. perform insertion into the link of adjacent faces
8888           while (true) {
8889             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8890             if ( adjElem )
8891               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8892             else
8893               break;
8894           }
8895           if (toCreatePolyedrs) {
8896             // perform insertion into the links of adjacent volumes
8897             UpdateVolumes(n12, n22, nodeList);
8898           }
8899           // 3. find an element appeared on n1 and n2 after the insertion
8900           insertMap.erase( elem );
8901           elem = findAdjacentFace( n1, n2, 0 );
8902         }
8903         if ( notFound || otherLink ) {
8904           // add element and nodes of the side into the insertMap
8905           insertMapIt = insertMap.insert
8906             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8907           (*insertMapIt).second.push_back( n1 );
8908           (*insertMapIt).second.push_back( n2 );
8909         }
8910         // add node to be inserted into elem
8911         (*insertMapIt).second.push_back( nIns );
8912         next[ 1 - intoBord ] = true;
8913       }
8914
8915       // go to the next segment
8916       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8917         if ( next[ iBord ] ) {
8918           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8919             eIt[ iBord ]++;
8920           nPrev[ iBord ] = *nIt[ iBord ];
8921           nIt[ iBord ]++; i[ iBord ]++;
8922         }
8923       }
8924     }
8925     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8926
8927     // perform insertion of nodes into elements
8928
8929     for (insertMapIt = insertMap.begin();
8930          insertMapIt != insertMap.end();
8931          insertMapIt++ )
8932     {
8933       const SMDS_MeshElement* elem = (*insertMapIt).first;
8934       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8935       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8936       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8937
8938       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8939
8940       if ( !theSideIsFreeBorder ) {
8941         // look for and insert nodes into the faces adjacent to elem
8942         while (true) {
8943           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8944           if ( adjElem )
8945             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8946           else
8947             break;
8948         }
8949       }
8950       if (toCreatePolyedrs) {
8951         // perform insertion into the links of adjacent volumes
8952         UpdateVolumes(n1, n2, nodeList);
8953       }
8954     }
8955
8956     delete param[0];
8957     delete param[1];
8958   } // end: insert new nodes
8959
8960   MergeNodes ( nodeGroupsToMerge );
8961
8962   return aResult;
8963 }
8964
8965 //=======================================================================
8966 //function : InsertNodesIntoLink
8967 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8968 //           and theBetweenNode2 and split theElement
8969 //=======================================================================
8970
8971 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8972                                            const SMDS_MeshNode*        theBetweenNode1,
8973                                            const SMDS_MeshNode*        theBetweenNode2,
8974                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8975                                            const bool                  toCreatePoly)
8976 {
8977   if ( theFace->GetType() != SMDSAbs_Face ) return;
8978
8979   // find indices of 2 link nodes and of the rest nodes
8980   int iNode = 0, il1, il2, i3, i4;
8981   il1 = il2 = i3 = i4 = -1;
8982   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8983   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8984
8985   if(theFace->IsQuadratic()) {
8986     const SMDS_VtkFace* F =
8987       dynamic_cast<const SMDS_VtkFace*>(theFace);
8988     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8989     // use special nodes iterator
8990     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8991     while( anIter->more() ) {
8992       const SMDS_MeshNode* n = cast2Node(anIter->next());
8993       if ( n == theBetweenNode1 )
8994         il1 = iNode;
8995       else if ( n == theBetweenNode2 )
8996         il2 = iNode;
8997       else if ( i3 < 0 )
8998         i3 = iNode;
8999       else
9000         i4 = iNode;
9001       nodes[ iNode++ ] = n;
9002     }
9003   }
9004   else {
9005     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9006     while ( nodeIt->more() ) {
9007       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9008       if ( n == theBetweenNode1 )
9009         il1 = iNode;
9010       else if ( n == theBetweenNode2 )
9011         il2 = iNode;
9012       else if ( i3 < 0 )
9013         i3 = iNode;
9014       else
9015         i4 = iNode;
9016       nodes[ iNode++ ] = n;
9017     }
9018   }
9019   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9020     return ;
9021
9022   // arrange link nodes to go one after another regarding the face orientation
9023   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9024   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9025   if ( reverse ) {
9026     iNode = il1;
9027     il1 = il2;
9028     il2 = iNode;
9029     aNodesToInsert.reverse();
9030   }
9031   // check that not link nodes of a quadrangles are in good order
9032   int nbFaceNodes = theFace->NbNodes();
9033   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9034     iNode = i3;
9035     i3 = i4;
9036     i4 = iNode;
9037   }
9038
9039   if (toCreatePoly || theFace->IsPoly()) {
9040
9041     iNode = 0;
9042     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9043
9044     // add nodes of face up to first node of link
9045     bool isFLN = false;
9046
9047     if(theFace->IsQuadratic()) {
9048       const SMDS_VtkFace* F =
9049         dynamic_cast<const SMDS_VtkFace*>(theFace);
9050       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9051       // use special nodes iterator
9052       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9053       while( anIter->more()  && !isFLN ) {
9054         const SMDS_MeshNode* n = cast2Node(anIter->next());
9055         poly_nodes[iNode++] = n;
9056         if (n == nodes[il1]) {
9057           isFLN = true;
9058         }
9059       }
9060       // add nodes to insert
9061       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9062       for (; nIt != aNodesToInsert.end(); nIt++) {
9063         poly_nodes[iNode++] = *nIt;
9064       }
9065       // add nodes of face starting from last node of link
9066       while ( anIter->more() ) {
9067         poly_nodes[iNode++] = cast2Node(anIter->next());
9068       }
9069     }
9070     else {
9071       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9072       while ( nodeIt->more() && !isFLN ) {
9073         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9074         poly_nodes[iNode++] = n;
9075         if (n == nodes[il1]) {
9076           isFLN = true;
9077         }
9078       }
9079       // add nodes to insert
9080       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9081       for (; nIt != aNodesToInsert.end(); nIt++) {
9082         poly_nodes[iNode++] = *nIt;
9083       }
9084       // add nodes of face starting from last node of link
9085       while ( nodeIt->more() ) {
9086         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9087         poly_nodes[iNode++] = n;
9088       }
9089     }
9090
9091     // edit or replace the face
9092     SMESHDS_Mesh *aMesh = GetMeshDS();
9093
9094     if (theFace->IsPoly()) {
9095       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9096     }
9097     else {
9098       int aShapeId = FindShape( theFace );
9099
9100       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9101       myLastCreatedElems.Append(newElem);
9102       if ( aShapeId && newElem )
9103         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9104
9105       aMesh->RemoveElement(theFace);
9106     }
9107     return;
9108   }
9109
9110   SMESHDS_Mesh *aMesh = GetMeshDS();
9111   if( !theFace->IsQuadratic() ) {
9112
9113     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9114     int nbLinkNodes = 2 + aNodesToInsert.size();
9115     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9116     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9117     linkNodes[ 0 ] = nodes[ il1 ];
9118     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9119     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9120     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9121       linkNodes[ iNode++ ] = *nIt;
9122     }
9123     // decide how to split a quadrangle: compare possible variants
9124     // and choose which of splits to be a quadrangle
9125     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9126     if ( nbFaceNodes == 3 ) {
9127       iBestQuad = nbSplits;
9128       i4 = i3;
9129     }
9130     else if ( nbFaceNodes == 4 ) {
9131       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9132       double aBestRate = DBL_MAX;
9133       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9134         i1 = 0; i2 = 1;
9135         double aBadRate = 0;
9136         // evaluate elements quality
9137         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9138           if ( iSplit == iQuad ) {
9139             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9140                                    linkNodes[ i2++ ],
9141                                    nodes[ i3 ],
9142                                    nodes[ i4 ]);
9143             aBadRate += getBadRate( &quad, aCrit );
9144           }
9145           else {
9146             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9147                                    linkNodes[ i2++ ],
9148                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9149             aBadRate += getBadRate( &tria, aCrit );
9150           }
9151         }
9152         // choice
9153         if ( aBadRate < aBestRate ) {
9154           iBestQuad = iQuad;
9155           aBestRate = aBadRate;
9156         }
9157       }
9158     }
9159
9160     // create new elements
9161     int aShapeId = FindShape( theFace );
9162
9163     i1 = 0; i2 = 1;
9164     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9165       SMDS_MeshElement* newElem = 0;
9166       if ( iSplit == iBestQuad )
9167         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9168                                   linkNodes[ i2++ ],
9169                                   nodes[ i3 ],
9170                                   nodes[ i4 ]);
9171       else
9172         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9173                                   linkNodes[ i2++ ],
9174                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9175       myLastCreatedElems.Append(newElem);
9176       if ( aShapeId && newElem )
9177         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9178     }
9179
9180     // change nodes of theFace
9181     const SMDS_MeshNode* newNodes[ 4 ];
9182     newNodes[ 0 ] = linkNodes[ i1 ];
9183     newNodes[ 1 ] = linkNodes[ i2 ];
9184     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9185     newNodes[ 3 ] = nodes[ i4 ];
9186     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9187     const SMDS_MeshElement* newElem = 0;
9188     if (iSplit == iBestQuad)
9189       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9190     else
9191       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9192     myLastCreatedElems.Append(newElem);
9193     if ( aShapeId && newElem )
9194       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9195 } // end if(!theFace->IsQuadratic())
9196   else { // theFace is quadratic
9197     // we have to split theFace on simple triangles and one simple quadrangle
9198     int tmp = il1/2;
9199     int nbshift = tmp*2;
9200     // shift nodes in nodes[] by nbshift
9201     int i,j;
9202     for(i=0; i<nbshift; i++) {
9203       const SMDS_MeshNode* n = nodes[0];
9204       for(j=0; j<nbFaceNodes-1; j++) {
9205         nodes[j] = nodes[j+1];
9206       }
9207       nodes[nbFaceNodes-1] = n;
9208     }
9209     il1 = il1 - nbshift;
9210     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9211     //   n0      n1     n2    n0      n1     n2
9212     //     +-----+-----+        +-----+-----+
9213     //      \         /         |           |
9214     //       \       /          |           |
9215     //      n5+     +n3       n7+           +n3
9216     //         \   /            |           |
9217     //          \ /             |           |
9218     //           +              +-----+-----+
9219     //           n4           n6      n5     n4
9220
9221     // create new elements
9222     int aShapeId = FindShape( theFace );
9223
9224     int n1,n2,n3;
9225     if(nbFaceNodes==6) { // quadratic triangle
9226       SMDS_MeshElement* newElem =
9227         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9228       myLastCreatedElems.Append(newElem);
9229       if ( aShapeId && newElem )
9230         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9231       if(theFace->IsMediumNode(nodes[il1])) {
9232         // create quadrangle
9233         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9234         myLastCreatedElems.Append(newElem);
9235         if ( aShapeId && newElem )
9236           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9237         n1 = 1;
9238         n2 = 2;
9239         n3 = 3;
9240       }
9241       else {
9242         // create quadrangle
9243         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9244         myLastCreatedElems.Append(newElem);
9245         if ( aShapeId && newElem )
9246           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9247         n1 = 0;
9248         n2 = 1;
9249         n3 = 5;
9250       }
9251     }
9252     else { // nbFaceNodes==8 - quadratic quadrangle
9253       SMDS_MeshElement* newElem =
9254         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9255       myLastCreatedElems.Append(newElem);
9256       if ( aShapeId && newElem )
9257         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9258       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9259       myLastCreatedElems.Append(newElem);
9260       if ( aShapeId && newElem )
9261         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9262       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9263       myLastCreatedElems.Append(newElem);
9264       if ( aShapeId && newElem )
9265         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9266       if(theFace->IsMediumNode(nodes[il1])) {
9267         // create quadrangle
9268         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9269         myLastCreatedElems.Append(newElem);
9270         if ( aShapeId && newElem )
9271           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9272         n1 = 1;
9273         n2 = 2;
9274         n3 = 3;
9275       }
9276       else {
9277         // create quadrangle
9278         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9279         myLastCreatedElems.Append(newElem);
9280         if ( aShapeId && newElem )
9281           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9282         n1 = 0;
9283         n2 = 1;
9284         n3 = 7;
9285       }
9286     }
9287     // create needed triangles using n1,n2,n3 and inserted nodes
9288     int nbn = 2 + aNodesToInsert.size();
9289     //const SMDS_MeshNode* aNodes[nbn];
9290     vector<const SMDS_MeshNode*> aNodes(nbn);
9291     aNodes[0] = nodes[n1];
9292     aNodes[nbn-1] = nodes[n2];
9293     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9294     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9295       aNodes[iNode++] = *nIt;
9296     }
9297     for(i=1; i<nbn; i++) {
9298       SMDS_MeshElement* newElem =
9299         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9300       myLastCreatedElems.Append(newElem);
9301       if ( aShapeId && newElem )
9302         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9303     }
9304   }
9305   // remove old face
9306   aMesh->RemoveElement(theFace);
9307 }
9308
9309 //=======================================================================
9310 //function : UpdateVolumes
9311 //purpose  :
9312 //=======================================================================
9313 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9314                                       const SMDS_MeshNode*        theBetweenNode2,
9315                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9316 {
9317   myLastCreatedElems.Clear();
9318   myLastCreatedNodes.Clear();
9319
9320   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9321   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9322     const SMDS_MeshElement* elem = invElemIt->next();
9323
9324     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9325     SMDS_VolumeTool aVolume (elem);
9326     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9327       continue;
9328
9329     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9330     int iface, nbFaces = aVolume.NbFaces();
9331     vector<const SMDS_MeshNode *> poly_nodes;
9332     vector<int> quantities (nbFaces);
9333
9334     for (iface = 0; iface < nbFaces; iface++) {
9335       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9336       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9337       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9338
9339       for (int inode = 0; inode < nbFaceNodes; inode++) {
9340         poly_nodes.push_back(faceNodes[inode]);
9341
9342         if (nbInserted == 0) {
9343           if (faceNodes[inode] == theBetweenNode1) {
9344             if (faceNodes[inode + 1] == theBetweenNode2) {
9345               nbInserted = theNodesToInsert.size();
9346
9347               // add nodes to insert
9348               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9349               for (; nIt != theNodesToInsert.end(); nIt++) {
9350                 poly_nodes.push_back(*nIt);
9351               }
9352             }
9353           }
9354           else if (faceNodes[inode] == theBetweenNode2) {
9355             if (faceNodes[inode + 1] == theBetweenNode1) {
9356               nbInserted = theNodesToInsert.size();
9357
9358               // add nodes to insert in reversed order
9359               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9360               nIt--;
9361               for (; nIt != theNodesToInsert.begin(); nIt--) {
9362                 poly_nodes.push_back(*nIt);
9363               }
9364               poly_nodes.push_back(*nIt);
9365             }
9366           }
9367           else {
9368           }
9369         }
9370       }
9371       quantities[iface] = nbFaceNodes + nbInserted;
9372     }
9373
9374     // Replace or update the volume
9375     SMESHDS_Mesh *aMesh = GetMeshDS();
9376
9377     if (elem->IsPoly()) {
9378       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9379
9380     }
9381     else {
9382       int aShapeId = FindShape( elem );
9383
9384       SMDS_MeshElement* newElem =
9385         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9386       myLastCreatedElems.Append(newElem);
9387       if (aShapeId && newElem)
9388         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9389
9390       aMesh->RemoveElement(elem);
9391     }
9392   }
9393 }
9394
9395 namespace
9396 {
9397   //================================================================================
9398   /*!
9399    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9400    */
9401   //================================================================================
9402
9403   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9404                            vector<const SMDS_MeshNode *> & nodes,
9405                            vector<int> &                   nbNodeInFaces )
9406   {
9407     nodes.clear();
9408     nbNodeInFaces.clear();
9409     SMDS_VolumeTool vTool ( elem );
9410     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9411     {
9412       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9413       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9414       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9415     }
9416   }
9417 }
9418
9419 //=======================================================================
9420 /*!
9421  * \brief Convert elements contained in a submesh to quadratic
9422  * \return int - nb of checked elements
9423  */
9424 //=======================================================================
9425
9426 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9427                                              SMESH_MesherHelper& theHelper,
9428                                              const bool          theForce3d)
9429 {
9430   int nbElem = 0;
9431   if( !theSm ) return nbElem;
9432
9433   vector<int> nbNodeInFaces;
9434   vector<const SMDS_MeshNode *> nodes;
9435   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9436   while(ElemItr->more())
9437   {
9438     nbElem++;
9439     const SMDS_MeshElement* elem = ElemItr->next();
9440     if( !elem || elem->IsQuadratic() ) continue;
9441
9442     // get elem data needed to re-create it
9443     //
9444     const int id                        = elem->GetID();
9445     const int nbNodes                   = elem->NbNodes();
9446     const SMDSAbs_ElementType aType     = elem->GetType();
9447     const SMDSAbs_EntityType  aGeomType = elem->GetEntityType();
9448     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9449     if ( aGeomType == SMDSEntity_Polyhedra )
9450       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9451     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9452       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9453
9454     // remove a linear element
9455     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9456
9457     const SMDS_MeshElement* NewElem = 0;
9458
9459     switch( aType )
9460     {
9461     case SMDSAbs_Edge :
9462       {
9463         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9464         break;
9465       }
9466     case SMDSAbs_Face :
9467       {
9468         switch(nbNodes)
9469         {
9470         case 3:
9471           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9472           break;
9473         case 4:
9474           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9475           break;
9476         default:
9477           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9478           continue;
9479         }
9480         break;
9481       }
9482     case SMDSAbs_Volume :
9483       {
9484         switch( aGeomType )
9485         {
9486         case SMDSEntity_Tetra:
9487           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9488           break;
9489         case SMDSEntity_Pyramid:
9490           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9491           break;
9492         case SMDSEntity_Penta:
9493           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9494           break;
9495         case SMDSEntity_Hexa:
9496           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9497                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9498           break;
9499         case SMDSEntity_Hexagonal_Prism:
9500         default:
9501           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9502         }
9503         break;
9504       }
9505     default :
9506       continue;
9507     }
9508     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9509     if( NewElem )
9510       theSm->AddElement( NewElem );
9511   }
9512   return nbElem;
9513 }
9514
9515 //=======================================================================
9516 //function : ConvertToQuadratic
9517 //purpose  :
9518 //=======================================================================
9519
9520 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9521 {
9522   SMESHDS_Mesh* meshDS = GetMeshDS();
9523
9524   SMESH_MesherHelper aHelper(*myMesh);
9525   aHelper.SetIsQuadratic( true );
9526
9527   int nbCheckedElems = 0;
9528   if ( myMesh->HasShapeToMesh() )
9529   {
9530     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9531     {
9532       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9533       while ( smIt->more() ) {
9534         SMESH_subMesh* sm = smIt->next();
9535         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9536           aHelper.SetSubShape( sm->GetSubShape() );
9537           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9538         }
9539       }
9540     }
9541   }
9542   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9543   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9544   {
9545     SMESHDS_SubMesh *smDS = 0;
9546     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9547     while(aEdgeItr->more())
9548     {
9549       const SMDS_MeshEdge* edge = aEdgeItr->next();
9550       if(edge && !edge->IsQuadratic())
9551       {
9552         int id = edge->GetID();
9553         //MESSAGE("edge->GetID() " << id);
9554         const SMDS_MeshNode* n1 = edge->GetNode(0);
9555         const SMDS_MeshNode* n2 = edge->GetNode(1);
9556
9557         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9558
9559         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9560         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9561       }
9562     }
9563     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9564     while(aFaceItr->more())
9565     {
9566       const SMDS_MeshFace* face = aFaceItr->next();
9567       if(!face || face->IsQuadratic() ) continue;
9568
9569       const int id = face->GetID();
9570       const SMDSAbs_EntityType type = face->GetEntityType();
9571       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9572
9573       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9574
9575       SMDS_MeshFace * NewFace = 0;
9576       switch( type )
9577       {
9578       case SMDSEntity_Triangle:
9579         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9580         break;
9581       case SMDSEntity_Quadrangle:
9582         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9583         break;
9584       default:
9585         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9586       }
9587       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9588     }
9589     vector<int> nbNodeInFaces;
9590     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9591     while(aVolumeItr->more())
9592     {
9593       const SMDS_MeshVolume* volume = aVolumeItr->next();
9594       if(!volume || volume->IsQuadratic() ) continue;
9595
9596       const int id = volume->GetID();
9597       const SMDSAbs_EntityType type = volume->GetEntityType();
9598       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9599       if ( type == SMDSEntity_Polyhedra )
9600         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9601       else if ( type == SMDSEntity_Hexagonal_Prism )
9602         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9603
9604       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9605
9606       SMDS_MeshVolume * NewVolume = 0;
9607       switch ( type )
9608       {
9609       case SMDSEntity_Tetra:
9610         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9611         break;
9612       case SMDSEntity_Hexa:
9613         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9614                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9615         break;
9616       case SMDSEntity_Pyramid:
9617         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9618                                       nodes[3], nodes[4], id, theForce3d);
9619         break;
9620       case SMDSEntity_Penta:
9621         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9622                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9623         break;
9624       case SMDSEntity_Hexagonal_Prism:
9625       default:
9626         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9627       }
9628       ReplaceElemInGroups(volume, NewVolume, meshDS);
9629     }
9630   }
9631
9632   if ( !theForce3d )
9633   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9634     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9635     aHelper.FixQuadraticElements();
9636   }
9637 }
9638
9639 //================================================================================
9640 /*!
9641  * \brief Makes given elements quadratic
9642  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9643  *  \param theElements - elements to make quadratic 
9644  */
9645 //================================================================================
9646
9647 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9648                                           TIDSortedElemSet& theElements)
9649 {
9650   if ( theElements.empty() ) return;
9651
9652   // we believe that all theElements are of the same type
9653   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9654   
9655   // get all nodes shared by theElements
9656   TIDSortedNodeSet allNodes;
9657   TIDSortedElemSet::iterator eIt = theElements.begin();
9658   for ( ; eIt != theElements.end(); ++eIt )
9659     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9660
9661   // complete theElements with elements of lower dim whose all nodes are in allNodes
9662
9663   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9664   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9665   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9666   for ( ; nIt != allNodes.end(); ++nIt )
9667   {
9668     const SMDS_MeshNode* n = *nIt;
9669     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9670     while ( invIt->more() )
9671     {
9672       const SMDS_MeshElement* e = invIt->next();
9673       if ( e->IsQuadratic() )
9674       {
9675         quadAdjacentElems[ e->GetType() ].insert( e );
9676         continue;
9677       }
9678       if ( e->GetType() >= elemType )
9679       {
9680         continue; // same type of more complex linear element
9681       }
9682
9683       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9684         continue; // e is already checked
9685
9686       // check nodes
9687       bool allIn = true;
9688       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9689       while ( nodeIt->more() && allIn )
9690         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9691       if ( allIn )
9692         theElements.insert(e );
9693     }
9694   }
9695
9696   SMESH_MesherHelper helper(*myMesh);
9697   helper.SetIsQuadratic( true );
9698
9699   // add links of quadratic adjacent elements to the helper
9700
9701   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9702     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9703           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9704     {
9705       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9706     }
9707   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9708     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9709           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9710     {
9711       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9712     }
9713   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9714     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9715           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9716     {
9717       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9718     }
9719
9720   // make quadratic elements instead of linear ones
9721
9722   SMESHDS_Mesh* meshDS = GetMeshDS();
9723   SMESHDS_SubMesh* smDS = 0;
9724   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9725   {
9726     const SMDS_MeshElement* elem = *eIt;
9727     if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9728       continue;
9729
9730     const int id                   = elem->GetID();
9731     const SMDSAbs_ElementType type = elem->GetType();
9732     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9733
9734     if ( !smDS || !smDS->Contains( elem ))
9735       smDS = meshDS->MeshElements( elem->getshapeId() );
9736     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9737
9738     SMDS_MeshElement * newElem = 0;
9739     switch( nodes.size() )
9740     {
9741     case 4: // cases for most frequently used element types go first (for optimization)
9742       if ( type == SMDSAbs_Volume )
9743         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9744       else
9745         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9746       break;
9747     case 8:
9748       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9749                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9750       break;
9751     case 3:
9752       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9753       break;
9754     case 2:
9755       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9756       break;
9757     case 5:
9758       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9759                                  nodes[4], id, theForce3d);
9760       break;
9761     case 6:
9762       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9763                                  nodes[4], nodes[5], id, theForce3d);
9764       break;
9765     default:;
9766     }
9767     ReplaceElemInGroups( elem, newElem, meshDS);
9768     if( newElem && smDS )
9769       smDS->AddElement( newElem );
9770   }
9771
9772   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9773   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9774     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9775     helper.FixQuadraticElements();
9776   }
9777 }
9778
9779 //=======================================================================
9780 /*!
9781  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9782  * \return int - nb of checked elements
9783  */
9784 //=======================================================================
9785
9786 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9787                                      SMDS_ElemIteratorPtr theItr,
9788                                      const int            theShapeID)
9789 {
9790   int nbElem = 0;
9791   SMESHDS_Mesh* meshDS = GetMeshDS();
9792
9793   while( theItr->more() )
9794   {
9795     const SMDS_MeshElement* elem = theItr->next();
9796     nbElem++;
9797     if( elem && elem->IsQuadratic())
9798     {
9799       int id                    = elem->GetID();
9800       int nbCornerNodes         = elem->NbCornerNodes();
9801       SMDSAbs_ElementType aType = elem->GetType();
9802
9803       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9804
9805       //remove a quadratic element
9806       if ( !theSm || !theSm->Contains( elem ))
9807         theSm = meshDS->MeshElements( elem->getshapeId() );
9808       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9809
9810       // remove medium nodes
9811       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9812         if ( nodes[i]->NbInverseElements() == 0 )
9813           meshDS->RemoveFreeNode( nodes[i], theSm );
9814
9815       // add a linear element
9816       nodes.resize( nbCornerNodes );
9817       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9818       ReplaceElemInGroups(elem, newElem, meshDS);
9819       if( theSm && newElem )
9820         theSm->AddElement( newElem );
9821     }
9822   }
9823   return nbElem;
9824 }
9825
9826 //=======================================================================
9827 //function : ConvertFromQuadratic
9828 //purpose  :
9829 //=======================================================================
9830
9831 bool SMESH_MeshEditor::ConvertFromQuadratic()
9832 {
9833   int nbCheckedElems = 0;
9834   if ( myMesh->HasShapeToMesh() )
9835   {
9836     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9837     {
9838       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9839       while ( smIt->more() ) {
9840         SMESH_subMesh* sm = smIt->next();
9841         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9842           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9843       }
9844     }
9845   }
9846
9847   int totalNbElems =
9848     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9849   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9850   {
9851     SMESHDS_SubMesh *aSM = 0;
9852     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9853   }
9854
9855   return true;
9856 }
9857
9858 namespace
9859 {
9860   //================================================================================
9861   /*!
9862    * \brief Return true if all medium nodes of the element are in the node set
9863    */
9864   //================================================================================
9865
9866   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9867   {
9868     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9869       if ( !nodeSet.count( elem->GetNode(i) ))
9870         return false;
9871     return true;
9872   }
9873 }
9874
9875 //================================================================================
9876 /*!
9877  * \brief Makes given elements linear
9878  */
9879 //================================================================================
9880
9881 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9882 {
9883   if ( theElements.empty() ) return;
9884
9885   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9886   set<int> mediumNodeIDs;
9887   TIDSortedElemSet::iterator eIt = theElements.begin();
9888   for ( ; eIt != theElements.end(); ++eIt )
9889   {
9890     const SMDS_MeshElement* e = *eIt;
9891     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9892       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9893   }
9894
9895   // replace given elements by linear ones
9896   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9897   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9898   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9899
9900   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9901   // except those elements sharing medium nodes of quadratic element whose medium nodes
9902   // are not all in mediumNodeIDs
9903
9904   // get remaining medium nodes
9905   TIDSortedNodeSet mediumNodes;
9906   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9907   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9908     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9909       mediumNodes.insert( mediumNodes.end(), n );
9910
9911   // find more quadratic elements to convert
9912   TIDSortedElemSet moreElemsToConvert;
9913   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9914   for ( ; nIt != mediumNodes.end(); ++nIt )
9915   {
9916     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9917     while ( invIt->more() )
9918     {
9919       const SMDS_MeshElement* e = invIt->next();
9920       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9921       {
9922         // find a more complex element including e and
9923         // whose medium nodes are not in mediumNodes
9924         bool complexFound = false;
9925         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9926         {
9927           SMDS_ElemIteratorPtr invIt2 =
9928             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9929           while ( invIt2->more() )
9930           {
9931             const SMDS_MeshElement* eComplex = invIt2->next();
9932             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9933             {
9934               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9935               if ( nbCommonNodes == e->NbNodes())
9936               {
9937                 complexFound = true;
9938                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9939                 break;
9940               }
9941             }
9942           }
9943         }
9944         if ( !complexFound )
9945           moreElemsToConvert.insert( e );
9946       }
9947     }
9948   }
9949   elemIt = SMDS_ElemIteratorPtr
9950     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9951   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9952 }
9953
9954 //=======================================================================
9955 //function : SewSideElements
9956 //purpose  :
9957 //=======================================================================
9958
9959 SMESH_MeshEditor::Sew_Error
9960 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9961                                    TIDSortedElemSet&    theSide2,
9962                                    const SMDS_MeshNode* theFirstNode1,
9963                                    const SMDS_MeshNode* theFirstNode2,
9964                                    const SMDS_MeshNode* theSecondNode1,
9965                                    const SMDS_MeshNode* theSecondNode2)
9966 {
9967   myLastCreatedElems.Clear();
9968   myLastCreatedNodes.Clear();
9969
9970   MESSAGE ("::::SewSideElements()");
9971   if ( theSide1.size() != theSide2.size() )
9972     return SEW_DIFF_NB_OF_ELEMENTS;
9973
9974   Sew_Error aResult = SEW_OK;
9975   // Algo:
9976   // 1. Build set of faces representing each side
9977   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9978   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9979
9980   // =======================================================================
9981   // 1. Build set of faces representing each side:
9982   // =======================================================================
9983   // a. build set of nodes belonging to faces
9984   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9985   // c. create temporary faces representing side of volumes if correspondent
9986   //    face does not exist
9987
9988   SMESHDS_Mesh* aMesh = GetMeshDS();
9989   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9990   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9991   TIDSortedElemSet             faceSet1, faceSet2;
9992   set<const SMDS_MeshElement*> volSet1,  volSet2;
9993   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9994   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9995   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9996   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9997   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9998   int iSide, iFace, iNode;
9999
10000   list<const SMDS_MeshElement* > tempFaceList;
10001   for ( iSide = 0; iSide < 2; iSide++ ) {
10002     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10003     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10004     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10005     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10006     set<const SMDS_MeshElement*>::iterator vIt;
10007     TIDSortedElemSet::iterator eIt;
10008     set<const SMDS_MeshNode*>::iterator    nIt;
10009
10010     // check that given nodes belong to given elements
10011     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10012     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10013     int firstIndex = -1, secondIndex = -1;
10014     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10015       const SMDS_MeshElement* elem = *eIt;
10016       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10017       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10018       if ( firstIndex > -1 && secondIndex > -1 ) break;
10019     }
10020     if ( firstIndex < 0 || secondIndex < 0 ) {
10021       // we can simply return until temporary faces created
10022       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10023     }
10024
10025     // -----------------------------------------------------------
10026     // 1a. Collect nodes of existing faces
10027     //     and build set of face nodes in order to detect missing
10028     //     faces corresponding to sides of volumes
10029     // -----------------------------------------------------------
10030
10031     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10032
10033     // loop on the given element of a side
10034     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10035       //const SMDS_MeshElement* elem = *eIt;
10036       const SMDS_MeshElement* elem = *eIt;
10037       if ( elem->GetType() == SMDSAbs_Face ) {
10038         faceSet->insert( elem );
10039         set <const SMDS_MeshNode*> faceNodeSet;
10040         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10041         while ( nodeIt->more() ) {
10042           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10043           nodeSet->insert( n );
10044           faceNodeSet.insert( n );
10045         }
10046         setOfFaceNodeSet.insert( faceNodeSet );
10047       }
10048       else if ( elem->GetType() == SMDSAbs_Volume )
10049         volSet->insert( elem );
10050     }
10051     // ------------------------------------------------------------------------------
10052     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10053     // ------------------------------------------------------------------------------
10054
10055     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10056       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10057       while ( fIt->more() ) { // loop on faces sharing a node
10058         const SMDS_MeshElement* f = fIt->next();
10059         if ( faceSet->find( f ) == faceSet->end() ) {
10060           // check if all nodes are in nodeSet and
10061           // complete setOfFaceNodeSet if they are
10062           set <const SMDS_MeshNode*> faceNodeSet;
10063           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10064           bool allInSet = true;
10065           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10066             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10067             if ( nodeSet->find( n ) == nodeSet->end() )
10068               allInSet = false;
10069             else
10070               faceNodeSet.insert( n );
10071           }
10072           if ( allInSet ) {
10073             faceSet->insert( f );
10074             setOfFaceNodeSet.insert( faceNodeSet );
10075           }
10076         }
10077       }
10078     }
10079
10080     // -------------------------------------------------------------------------
10081     // 1c. Create temporary faces representing sides of volumes if correspondent
10082     //     face does not exist
10083     // -------------------------------------------------------------------------
10084
10085     if ( !volSet->empty() ) {
10086       //int nodeSetSize = nodeSet->size();
10087
10088       // loop on given volumes
10089       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10090         SMDS_VolumeTool vol (*vIt);
10091         // loop on volume faces: find free faces
10092         // --------------------------------------
10093         list<const SMDS_MeshElement* > freeFaceList;
10094         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10095           if ( !vol.IsFreeFace( iFace ))
10096             continue;
10097           // check if there is already a face with same nodes in a face set
10098           const SMDS_MeshElement* aFreeFace = 0;
10099           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10100           int nbNodes = vol.NbFaceNodes( iFace );
10101           set <const SMDS_MeshNode*> faceNodeSet;
10102           vol.GetFaceNodes( iFace, faceNodeSet );
10103           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10104           if ( isNewFace ) {
10105             // no such a face is given but it still can exist, check it
10106             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10107             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10108           }
10109           if ( !aFreeFace ) {
10110             // create a temporary face
10111             if ( nbNodes == 3 ) {
10112               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10113               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10114             }
10115             else if ( nbNodes == 4 ) {
10116               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10117               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10118             }
10119             else {
10120               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10121               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10122               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10123             }
10124             if ( aFreeFace )
10125               tempFaceList.push_back( aFreeFace );
10126           }
10127
10128           if ( aFreeFace )
10129             freeFaceList.push_back( aFreeFace );
10130
10131         } // loop on faces of a volume
10132
10133         // choose one of several free faces of a volume
10134         // --------------------------------------------
10135         if ( freeFaceList.size() > 1 ) {
10136           // choose a face having max nb of nodes shared by other elems of a side
10137           int maxNbNodes = -1;
10138           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10139           while ( fIt != freeFaceList.end() ) { // loop on free faces
10140             int nbSharedNodes = 0;
10141             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10142             while ( nodeIt->more() ) { // loop on free face nodes
10143               const SMDS_MeshNode* n =
10144                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10145               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10146               while ( invElemIt->more() ) {
10147                 const SMDS_MeshElement* e = invElemIt->next();
10148                 nbSharedNodes += faceSet->count( e );
10149                 nbSharedNodes += elemSet->count( e );
10150               }
10151             }
10152             if ( nbSharedNodes > maxNbNodes ) {
10153               maxNbNodes = nbSharedNodes;
10154               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10155             }
10156             else if ( nbSharedNodes == maxNbNodes ) {
10157               fIt++;
10158             }
10159             else {
10160               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10161             }
10162           }
10163           if ( freeFaceList.size() > 1 )
10164           {
10165             // could not choose one face, use another way
10166             // choose a face most close to the bary center of the opposite side
10167             gp_XYZ aBC( 0., 0., 0. );
10168             set <const SMDS_MeshNode*> addedNodes;
10169             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10170             eIt = elemSet2->begin();
10171             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10172               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10173               while ( nodeIt->more() ) { // loop on free face nodes
10174                 const SMDS_MeshNode* n =
10175                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10176                 if ( addedNodes.insert( n ).second )
10177                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10178               }
10179             }
10180             aBC /= addedNodes.size();
10181             double minDist = DBL_MAX;
10182             fIt = freeFaceList.begin();
10183             while ( fIt != freeFaceList.end() ) { // loop on free faces
10184               double dist = 0;
10185               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10186               while ( nodeIt->more() ) { // loop on free face nodes
10187                 const SMDS_MeshNode* n =
10188                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10189                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10190                 dist += ( aBC - p ).SquareModulus();
10191               }
10192               if ( dist < minDist ) {
10193                 minDist = dist;
10194                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10195               }
10196               else
10197                 fIt = freeFaceList.erase( fIt++ );
10198             }
10199           }
10200         } // choose one of several free faces of a volume
10201
10202         if ( freeFaceList.size() == 1 ) {
10203           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10204           faceSet->insert( aFreeFace );
10205           // complete a node set with nodes of a found free face
10206           //           for ( iNode = 0; iNode < ; iNode++ )
10207           //             nodeSet->insert( fNodes[ iNode ] );
10208         }
10209
10210       } // loop on volumes of a side
10211
10212       //       // complete a set of faces if new nodes in a nodeSet appeared
10213       //       // ----------------------------------------------------------
10214       //       if ( nodeSetSize != nodeSet->size() ) {
10215       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10216       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10217       //           while ( fIt->more() ) { // loop on faces sharing a node
10218       //             const SMDS_MeshElement* f = fIt->next();
10219       //             if ( faceSet->find( f ) == faceSet->end() ) {
10220       //               // check if all nodes are in nodeSet and
10221       //               // complete setOfFaceNodeSet if they are
10222       //               set <const SMDS_MeshNode*> faceNodeSet;
10223       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10224       //               bool allInSet = true;
10225       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10226       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10227       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10228       //                   allInSet = false;
10229       //                 else
10230       //                   faceNodeSet.insert( n );
10231       //               }
10232       //               if ( allInSet ) {
10233       //                 faceSet->insert( f );
10234       //                 setOfFaceNodeSet.insert( faceNodeSet );
10235       //               }
10236       //             }
10237       //           }
10238       //         }
10239       //       }
10240     } // Create temporary faces, if there are volumes given
10241   } // loop on sides
10242
10243   if ( faceSet1.size() != faceSet2.size() ) {
10244     // delete temporary faces: they are in reverseElements of actual nodes
10245 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10246 //    while ( tmpFaceIt->more() )
10247 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10248 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10249 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10250 //      aMesh->RemoveElement(*tmpFaceIt);
10251     MESSAGE("Diff nb of faces");
10252     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10253   }
10254
10255   // ============================================================
10256   // 2. Find nodes to merge:
10257   //              bind a node to remove to a node to put instead
10258   // ============================================================
10259
10260   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10261   if ( theFirstNode1 != theFirstNode2 )
10262     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10263   if ( theSecondNode1 != theSecondNode2 )
10264     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10265
10266   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10267   set< long > linkIdSet; // links to process
10268   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10269
10270   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10271   list< NLink > linkList[2];
10272   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10273   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10274   // loop on links in linkList; find faces by links and append links
10275   // of the found faces to linkList
10276   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10277   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10278   {
10279     NLink link[] = { *linkIt[0], *linkIt[1] };
10280     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10281     if ( !linkIdSet.count( linkID ) )
10282       continue;
10283
10284     // by links, find faces in the face sets,
10285     // and find indices of link nodes in the found faces;
10286     // in a face set, there is only one or no face sharing a link
10287     // ---------------------------------------------------------------
10288
10289     const SMDS_MeshElement* face[] = { 0, 0 };
10290     vector<const SMDS_MeshNode*> fnodes[2];
10291     int iLinkNode[2][2];
10292     TIDSortedElemSet avoidSet;
10293     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10294       const SMDS_MeshNode* n1 = link[iSide].first;
10295       const SMDS_MeshNode* n2 = link[iSide].second;
10296       //cout << "Side " << iSide << " ";
10297       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10298       // find a face by two link nodes
10299       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10300                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10301       if ( face[ iSide ])
10302       {
10303         //cout << " F " << face[ iSide]->GetID() <<endl;
10304         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10305         // put face nodes to fnodes
10306         if ( face[ iSide ]->IsQuadratic() )
10307         {
10308           // use interlaced nodes iterator
10309           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10310           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10311           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10312           while ( nIter->more() )
10313             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10314         }
10315         else
10316         {
10317           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10318                                   face[ iSide ]->end_nodes() );
10319         }
10320         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10321       }
10322     }
10323
10324     // check similarity of elements of the sides
10325     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10326       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10327       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10328         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10329       }
10330       else {
10331         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10332       }
10333       break; // do not return because it's necessary to remove tmp faces
10334     }
10335
10336     // set nodes to merge
10337     // -------------------
10338
10339     if ( face[0] && face[1] )  {
10340       const int nbNodes = face[0]->NbNodes();
10341       if ( nbNodes != face[1]->NbNodes() ) {
10342         MESSAGE("Diff nb of face nodes");
10343         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10344         break; // do not return because it s necessary to remove tmp faces
10345       }
10346       bool reverse[] = { false, false }; // order of nodes in the link
10347       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10348         // analyse link orientation in faces
10349         int i1 = iLinkNode[ iSide ][ 0 ];
10350         int i2 = iLinkNode[ iSide ][ 1 ];
10351         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10352       }
10353       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10354       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10355       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10356       {
10357         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10358                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10359       }
10360
10361       // add other links of the faces to linkList
10362       // -----------------------------------------
10363
10364       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10365         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10366         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10367         if ( !iter_isnew.second ) { // already in a set: no need to process
10368           linkIdSet.erase( iter_isnew.first );
10369         }
10370         else // new in set == encountered for the first time: add
10371         {
10372           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10373           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10374           linkList[0].push_back ( NLink( n1, n2 ));
10375           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10376         }
10377       }
10378     } // 2 faces found
10379
10380     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10381       break;
10382
10383   } // loop on link lists
10384
10385   if ( aResult == SEW_OK &&
10386        ( //linkIt[0] != linkList[0].end() ||
10387          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10388     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10389              " " << (faceSetPtr[1]->empty()));
10390     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10391   }
10392
10393   // ====================================================================
10394   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10395   // ====================================================================
10396
10397   // delete temporary faces
10398 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10399 //  while ( tmpFaceIt->more() )
10400 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10401   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10402   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10403     aMesh->RemoveElement(*tmpFaceIt);
10404
10405   if ( aResult != SEW_OK)
10406     return aResult;
10407
10408   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10409   // loop on nodes replacement map
10410   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10411   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10412     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10413       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10414       nodeIDsToRemove.push_back( nToRemove->GetID() );
10415       // loop on elements sharing nToRemove
10416       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10417       while ( invElemIt->more() ) {
10418         const SMDS_MeshElement* e = invElemIt->next();
10419         // get a new suite of nodes: make replacement
10420         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10421         vector< const SMDS_MeshNode*> nodes( nbNodes );
10422         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10423         while ( nIt->more() ) {
10424           const SMDS_MeshNode* n =
10425             static_cast<const SMDS_MeshNode*>( nIt->next() );
10426           nnIt = nReplaceMap.find( n );
10427           if ( nnIt != nReplaceMap.end() ) {
10428             nbReplaced++;
10429             n = (*nnIt).second;
10430           }
10431           nodes[ i++ ] = n;
10432         }
10433         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10434         //         elemIDsToRemove.push_back( e->GetID() );
10435         //       else
10436         if ( nbReplaced )
10437           {
10438             SMDSAbs_ElementType etyp = e->GetType();
10439             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10440             if (newElem)
10441               {
10442                 myLastCreatedElems.Append(newElem);
10443                 AddToSameGroups(newElem, e, aMesh);
10444                 int aShapeId = e->getshapeId();
10445                 if ( aShapeId )
10446                   {
10447                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10448                   }
10449               }
10450             aMesh->RemoveElement(e);
10451           }
10452       }
10453     }
10454
10455   Remove( nodeIDsToRemove, true );
10456
10457   return aResult;
10458 }
10459
10460 //================================================================================
10461 /*!
10462  * \brief Find corresponding nodes in two sets of faces
10463  * \param theSide1 - first face set
10464  * \param theSide2 - second first face
10465  * \param theFirstNode1 - a boundary node of set 1
10466  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10467  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10468  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10469  * \param nReplaceMap - output map of corresponding nodes
10470  * \return bool  - is a success or not
10471  */
10472 //================================================================================
10473
10474 #ifdef _DEBUG_
10475 //#define DEBUG_MATCHING_NODES
10476 #endif
10477
10478 SMESH_MeshEditor::Sew_Error
10479 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10480                                     set<const SMDS_MeshElement*>& theSide2,
10481                                     const SMDS_MeshNode*          theFirstNode1,
10482                                     const SMDS_MeshNode*          theFirstNode2,
10483                                     const SMDS_MeshNode*          theSecondNode1,
10484                                     const SMDS_MeshNode*          theSecondNode2,
10485                                     TNodeNodeMap &                nReplaceMap)
10486 {
10487   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10488
10489   nReplaceMap.clear();
10490   if ( theFirstNode1 != theFirstNode2 )
10491     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10492   if ( theSecondNode1 != theSecondNode2 )
10493     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10494
10495   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10496   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10497
10498   list< NLink > linkList[2];
10499   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10500   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10501
10502   // loop on links in linkList; find faces by links and append links
10503   // of the found faces to linkList
10504   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10505   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10506     NLink link[] = { *linkIt[0], *linkIt[1] };
10507     if ( linkSet.find( link[0] ) == linkSet.end() )
10508       continue;
10509
10510     // by links, find faces in the face sets,
10511     // and find indices of link nodes in the found faces;
10512     // in a face set, there is only one or no face sharing a link
10513     // ---------------------------------------------------------------
10514
10515     const SMDS_MeshElement* face[] = { 0, 0 };
10516     list<const SMDS_MeshNode*> notLinkNodes[2];
10517     //bool reverse[] = { false, false }; // order of notLinkNodes
10518     int nbNodes[2];
10519     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10520     {
10521       const SMDS_MeshNode* n1 = link[iSide].first;
10522       const SMDS_MeshNode* n2 = link[iSide].second;
10523       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10524       set< const SMDS_MeshElement* > facesOfNode1;
10525       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10526       {
10527         // during a loop of the first node, we find all faces around n1,
10528         // during a loop of the second node, we find one face sharing both n1 and n2
10529         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10530         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10531         while ( fIt->more() ) { // loop on faces sharing a node
10532           const SMDS_MeshElement* f = fIt->next();
10533           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10534               ! facesOfNode1.insert( f ).second ) // f encounters twice
10535           {
10536             if ( face[ iSide ] ) {
10537               MESSAGE( "2 faces per link " );
10538               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10539             }
10540             face[ iSide ] = f;
10541             faceSet->erase( f );
10542
10543             // get not link nodes
10544             int nbN = f->NbNodes();
10545             if ( f->IsQuadratic() )
10546               nbN /= 2;
10547             nbNodes[ iSide ] = nbN;
10548             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10549             int i1 = f->GetNodeIndex( n1 );
10550             int i2 = f->GetNodeIndex( n2 );
10551             int iEnd = nbN, iBeg = -1, iDelta = 1;
10552             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10553             if ( reverse ) {
10554               std::swap( iEnd, iBeg ); iDelta = -1;
10555             }
10556             int i = i2;
10557             while ( true ) {
10558               i += iDelta;
10559               if ( i == iEnd ) i = iBeg + iDelta;
10560               if ( i == i1 ) break;
10561               nodes.push_back ( f->GetNode( i ) );
10562             }
10563           }
10564         }
10565       }
10566     }
10567     // check similarity of elements of the sides
10568     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10569       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10570       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10571         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10572       }
10573       else {
10574         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10575       }
10576     }
10577
10578     // set nodes to merge
10579     // -------------------
10580
10581     if ( face[0] && face[1] )  {
10582       if ( nbNodes[0] != nbNodes[1] ) {
10583         MESSAGE("Diff nb of face nodes");
10584         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10585       }
10586 #ifdef DEBUG_MATCHING_NODES
10587       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10588                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10589                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10590 #endif
10591       int nbN = nbNodes[0];
10592       {
10593         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10594         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10595         for ( int i = 0 ; i < nbN - 2; ++i ) {
10596 #ifdef DEBUG_MATCHING_NODES
10597           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10598 #endif
10599           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10600         }
10601       }
10602
10603       // add other links of the face 1 to linkList
10604       // -----------------------------------------
10605
10606       const SMDS_MeshElement* f0 = face[0];
10607       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10608       for ( int i = 0; i < nbN; i++ )
10609       {
10610         const SMDS_MeshNode* n2 = f0->GetNode( i );
10611         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10612           linkSet.insert( SMESH_TLink( n1, n2 ));
10613         if ( !iter_isnew.second ) { // already in a set: no need to process
10614           linkSet.erase( iter_isnew.first );
10615         }
10616         else // new in set == encountered for the first time: add
10617         {
10618 #ifdef DEBUG_MATCHING_NODES
10619           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10620                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10621 #endif
10622           linkList[0].push_back ( NLink( n1, n2 ));
10623           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10624         }
10625         n1 = n2;
10626       }
10627     } // 2 faces found
10628   } // loop on link lists
10629
10630   return SEW_OK;
10631 }
10632
10633 //================================================================================
10634 /*!
10635   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10636   \param theElems - the list of elements (edges or faces) to be replicated
10637   The nodes for duplication could be found from these elements
10638   \param theNodesNot - list of nodes to NOT replicate
10639   \param theAffectedElems - the list of elements (cells and edges) to which the 
10640   replicated nodes should be associated to.
10641   \return TRUE if operation has been completed successfully, FALSE otherwise
10642 */
10643 //================================================================================
10644
10645 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10646                                     const TIDSortedElemSet& theNodesNot,
10647                                     const TIDSortedElemSet& theAffectedElems )
10648 {
10649   myLastCreatedElems.Clear();
10650   myLastCreatedNodes.Clear();
10651
10652   if ( theElems.size() == 0 )
10653     return false;
10654
10655   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10656   if ( !aMeshDS )
10657     return false;
10658
10659   bool res = false;
10660   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10661   // duplicate elements and nodes
10662   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10663   // replce nodes by duplications
10664   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10665   return res;
10666 }
10667
10668 //================================================================================
10669 /*!
10670   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10671   \param theMeshDS - mesh instance
10672   \param theElems - the elements replicated or modified (nodes should be changed)
10673   \param theNodesNot - nodes to NOT replicate
10674   \param theNodeNodeMap - relation of old node to new created node
10675   \param theIsDoubleElem - flag os to replicate element or modify
10676   \return TRUE if operation has been completed successfully, FALSE otherwise
10677 */
10678 //================================================================================
10679
10680 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10681                                     const TIDSortedElemSet& theElems,
10682                                     const TIDSortedElemSet& theNodesNot,
10683                                     std::map< const SMDS_MeshNode*,
10684                                     const SMDS_MeshNode* >& theNodeNodeMap,
10685                                     const bool theIsDoubleElem )
10686 {
10687   MESSAGE("doubleNodes");
10688   // iterate on through element and duplicate them (by nodes duplication)
10689   bool res = false;
10690   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10691   for ( ;  elemItr != theElems.end(); ++elemItr )
10692   {
10693     const SMDS_MeshElement* anElem = *elemItr;
10694     if (!anElem)
10695       continue;
10696
10697     bool isDuplicate = false;
10698     // duplicate nodes to duplicate element
10699     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10700     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10701     int ind = 0;
10702     while ( anIter->more() ) 
10703     { 
10704
10705       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10706       SMDS_MeshNode* aNewNode = aCurrNode;
10707       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10708         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10709       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10710       {
10711         // duplicate node
10712         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10713         theNodeNodeMap[ aCurrNode ] = aNewNode;
10714         myLastCreatedNodes.Append( aNewNode );
10715       }
10716       isDuplicate |= (aCurrNode != aNewNode);
10717       newNodes[ ind++ ] = aNewNode;
10718     }
10719     if ( !isDuplicate )
10720       continue;
10721
10722     if ( theIsDoubleElem )
10723       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10724     else
10725       {
10726       MESSAGE("ChangeElementNodes");
10727       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10728       }
10729     res = true;
10730   }
10731   return res;
10732 }
10733
10734 //================================================================================
10735 /*!
10736   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10737   \param theNodes - identifiers of nodes to be doubled
10738   \param theModifiedElems - identifiers of elements to be updated by the new (doubled) 
10739          nodes. If list of element identifiers is empty then nodes are doubled but 
10740          they not assigned to elements
10741   \return TRUE if operation has been completed successfully, FALSE otherwise
10742 */
10743 //================================================================================
10744
10745 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes, 
10746                                     const std::list< int >& theListOfModifiedElems )
10747 {
10748   MESSAGE("DoubleNodes");
10749   myLastCreatedElems.Clear();
10750   myLastCreatedNodes.Clear();
10751
10752   if ( theListOfNodes.size() == 0 )
10753     return false;
10754
10755   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10756   if ( !aMeshDS )
10757     return false;
10758
10759   // iterate through nodes and duplicate them
10760
10761   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10762
10763   std::list< int >::const_iterator aNodeIter;
10764   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10765   {
10766     int aCurr = *aNodeIter;
10767     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10768     if ( !aNode )
10769       continue;
10770
10771     // duplicate node
10772
10773     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10774     if ( aNewNode )
10775     {
10776       anOldNodeToNewNode[ aNode ] = aNewNode;
10777       myLastCreatedNodes.Append( aNewNode );
10778     }
10779   }
10780
10781   // Create map of new nodes for modified elements
10782
10783   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10784
10785   std::list< int >::const_iterator anElemIter;
10786   for ( anElemIter = theListOfModifiedElems.begin(); 
10787         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10788   {
10789     int aCurr = *anElemIter;
10790     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10791     if ( !anElem )
10792       continue;
10793
10794     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10795
10796     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10797     int ind = 0;
10798     while ( anIter->more() ) 
10799     { 
10800       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10801       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10802       {
10803         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10804         aNodeArr[ ind++ ] = aNewNode;
10805       }
10806       else
10807         aNodeArr[ ind++ ] = aCurrNode;
10808     }
10809     anElemToNodes[ anElem ] = aNodeArr;
10810   }
10811
10812   // Change nodes of elements  
10813
10814   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10815     anElemToNodesIter = anElemToNodes.begin();
10816   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10817   {
10818     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10819     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10820     if ( anElem )
10821       {
10822       MESSAGE("ChangeElementNodes");
10823       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10824       }
10825   }
10826
10827   return true;
10828 }
10829
10830 namespace {
10831
10832   //================================================================================
10833   /*!
10834   \brief Check if element located inside shape
10835   \return TRUE if IN or ON shape, FALSE otherwise
10836   */
10837   //================================================================================
10838
10839   template<class Classifier>
10840   bool isInside(const SMDS_MeshElement* theElem,
10841                 Classifier&             theClassifier,
10842                 const double            theTol)
10843   {
10844     gp_XYZ centerXYZ (0, 0, 0);
10845     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10846     while (aNodeItr->more())
10847       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10848
10849     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10850     theClassifier.Perform(aPnt, theTol);
10851     TopAbs_State aState = theClassifier.State();
10852     return (aState == TopAbs_IN || aState == TopAbs_ON );
10853   }
10854
10855   //================================================================================
10856   /*!
10857    * \brief Classifier of the 3D point on the TopoDS_Face
10858    *        with interaface suitable for isInside()
10859    */
10860   //================================================================================
10861
10862   struct _FaceClassifier
10863   {
10864     Extrema_ExtPS       _extremum;
10865     BRepAdaptor_Surface _surface;
10866     TopAbs_State        _state;
10867
10868     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10869     {
10870       _extremum.Initialize( _surface,
10871                             _surface.FirstUParameter(), _surface.LastUParameter(),
10872                             _surface.FirstVParameter(), _surface.LastVParameter(),
10873                             _surface.Tolerance(), _surface.Tolerance() );
10874     }
10875     void Perform(const gp_Pnt& aPnt, double theTol)
10876     {
10877       _state = TopAbs_OUT;
10878       _extremum.Perform(aPnt);
10879       if ( _extremum.IsDone() )
10880         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10881 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10882           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10883 #else
10884           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10885 #endif
10886     }
10887     TopAbs_State State() const
10888     {
10889       return _state;
10890     }
10891   };
10892 }
10893
10894 //================================================================================
10895 /*!
10896   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10897   \param theElems - group of of elements (edges or faces) to be replicated
10898   \param theNodesNot - group of nodes not to replicate
10899   \param theShape - shape to detect affected elements (element which geometric center
10900   located on or inside shape).
10901   The replicated nodes should be associated to affected elements.
10902   \return TRUE if operation has been completed successfully, FALSE otherwise
10903 */
10904 //================================================================================
10905
10906 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10907                                             const TIDSortedElemSet& theNodesNot,
10908                                             const TopoDS_Shape&     theShape )
10909 {
10910   if ( theShape.IsNull() )
10911     return false;
10912
10913   const double aTol = Precision::Confusion();
10914   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10915   auto_ptr<_FaceClassifier>              aFaceClassifier;
10916   if ( theShape.ShapeType() == TopAbs_SOLID )
10917   {
10918     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10919     bsc3d->PerformInfinitePoint(aTol);
10920   }
10921   else if (theShape.ShapeType() == TopAbs_FACE )
10922   {
10923     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10924   }
10925
10926   // iterates on indicated elements and get elements by back references from their nodes
10927   TIDSortedElemSet anAffected;
10928   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10929   for ( ;  elemItr != theElems.end(); ++elemItr )
10930   {
10931     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10932     if (!anElem)
10933       continue;
10934
10935     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10936     while ( nodeItr->more() )
10937     {
10938       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10939       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10940         continue;
10941       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10942       while ( backElemItr->more() )
10943       {
10944         const SMDS_MeshElement* curElem = backElemItr->next();
10945         if ( curElem && theElems.find(curElem) == theElems.end() &&
10946              ( bsc3d.get() ?
10947                isInside( curElem, *bsc3d, aTol ) :
10948                isInside( curElem, *aFaceClassifier, aTol )))
10949           anAffected.insert( curElem );
10950       }
10951     }
10952   }
10953   return DoubleNodes( theElems, theNodesNot, anAffected );
10954 }
10955
10956 /*!
10957  *  \brief compute an oriented angle between two planes defined by four points.
10958  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10959  *  @param p0 base of the rotation axe
10960  *  @param p1 extremity of the rotation axe
10961  *  @param g1 belongs to the first plane
10962  *  @param g2 belongs to the second plane
10963  */
10964 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10965 {
10966 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10967 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10968 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10969 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10970   gp_Vec vref(p0, p1);
10971   gp_Vec v1(p0, g1);
10972   gp_Vec v2(p0, g2);
10973   gp_Vec n1 = vref.Crossed(v1);
10974   gp_Vec n2 = vref.Crossed(v2);
10975   return n2.AngleWithRef(n1, vref);
10976 }
10977
10978 /*!
10979  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10980  * The list of groups must describe a partition of the mesh volumes.
10981  * The nodes of the internal faces at the boundaries of the groups are doubled.
10982  * In option, the internal faces are replaced by flat elements.
10983  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10984  * The flat elements are stored in groups of volumes.
10985  * @param theElems - list of groups of volumes, where a group of volume is a set of
10986  * SMDS_MeshElements sorted by Id.
10987  * @param createJointElems - if TRUE, create the elements
10988  * @return TRUE if operation has been completed successfully, FALSE otherwise
10989  */
10990 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10991                                                      bool createJointElems)
10992 {
10993   MESSAGE("----------------------------------------------");
10994   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10995   MESSAGE("----------------------------------------------");
10996
10997   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10998   meshDS->BuildDownWardConnectivity(true);
10999   CHRONO(50);
11000   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11001
11002   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11003   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11004   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11005
11006   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11007   std::map<int,int>celldom; // cell vtkId --> domain
11008   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11009   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11010   faceDomains.clear();
11011   celldom.clear();
11012   cellDomains.clear();
11013   nodeDomains.clear();
11014   std::map<int,int> emptyMap;
11015   std::set<int> emptySet;
11016   emptyMap.clear();
11017
11018   for (int idom = 0; idom < theElems.size(); idom++)
11019     {
11020
11021       // --- build a map (face to duplicate --> volume to modify)
11022       //     with all the faces shared by 2 domains (group of elements)
11023       //     and corresponding volume of this domain, for each shared face.
11024       //     a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
11025
11026       //MESSAGE("Domain " << idom);
11027       const TIDSortedElemSet& domain = theElems[idom];
11028       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11029       for (; elemItr != domain.end(); ++elemItr)
11030         {
11031           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11032           if (!anElem)
11033             continue;
11034           int vtkId = anElem->getVtkId();
11035           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11036           int neighborsVtkIds[NBMAXNEIGHBORS];
11037           int downIds[NBMAXNEIGHBORS];
11038           unsigned char downTypes[NBMAXNEIGHBORS];
11039           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11040           for (int n = 0; n < nbNeighbors; n++)
11041             {
11042               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11043               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11044               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11045                 {
11046                   DownIdType face(downIds[n], downTypes[n]);
11047                   if (!faceDomains.count(face))
11048                     faceDomains[face] = emptyMap; // create an empty entry for face
11049                   if (!faceDomains[face].count(idom))
11050                     {
11051                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11052                       celldom[vtkId] = idom;
11053                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11054                     }
11055                 }
11056             }
11057         }
11058     }
11059
11060   //MESSAGE("Number of shared faces " << faceDomains.size());
11061   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11062
11063   // --- explore the shared faces domain by domain,
11064   //     explore the nodes of the face and see if they belong to a cell in the domain,
11065   //     which has only a node or an edge on the border (not a shared face)
11066
11067   for (int idomain = 0; idomain < theElems.size(); idomain++)
11068     {
11069       //MESSAGE("Domain " << idomain);
11070       const TIDSortedElemSet& domain = theElems[idomain];
11071       itface = faceDomains.begin();
11072       for (; itface != faceDomains.end(); ++itface)
11073         {
11074           std::map<int, int> domvol = itface->second;
11075           if (!domvol.count(idomain))
11076             continue;
11077           DownIdType face = itface->first;
11078           //MESSAGE(" --- face " << face.cellId);
11079           std::set<int> oldNodes;
11080           oldNodes.clear();
11081           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11082           std::set<int>::iterator itn = oldNodes.begin();
11083           for (; itn != oldNodes.end(); ++itn)
11084             {
11085               int oldId = *itn;
11086               //MESSAGE("     node " << oldId);
11087               std::set<int> cells;
11088               cells.clear();
11089               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11090               for (int i=0; i<l.ncells; i++)
11091                 {
11092                   int vtkId = l.cells[i];
11093                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11094                   if (!domain.count(anElem))
11095                     continue;
11096                   int vtkType = grid->GetCellType(vtkId);
11097                   int downId = grid->CellIdToDownId(vtkId);
11098                   if (downId < 0)
11099                     {
11100                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11101                       continue; // not OK at this stage of the algorithm:
11102                                 //no cells created after BuildDownWardConnectivity
11103                     }
11104                   DownIdType aCell(downId, vtkType);
11105                   if (celldom.count(vtkId))
11106                     continue;
11107                   cellDomains[aCell][idomain] = vtkId;
11108                   celldom[vtkId] = idomain;
11109                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11110                 }
11111             }
11112         }
11113     }
11114
11115   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11116   //     for each shared face, get the nodes
11117   //     for each node, for each domain of the face, create a clone of the node
11118
11119   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11120   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11121   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11122
11123   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11124   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11125   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11126
11127   for (int idomain = 0; idomain < theElems.size(); idomain++)
11128     {
11129       itface = faceDomains.begin();
11130       for (; itface != faceDomains.end(); ++itface)
11131         {
11132           std::map<int, int> domvol = itface->second;
11133           if (!domvol.count(idomain))
11134             continue;
11135           DownIdType face = itface->first;
11136           //MESSAGE(" --- face " << face.cellId);
11137           std::set<int> oldNodes;
11138           oldNodes.clear();
11139           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11140           bool isMultipleDetected = false;
11141           std::set<int>::iterator itn = oldNodes.begin();
11142           for (; itn != oldNodes.end(); ++itn)
11143             {
11144               int oldId = *itn;
11145               //MESSAGE("     node " << oldId);
11146               if (!nodeDomains.count(oldId))
11147                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11148               if (nodeDomains[oldId].empty())
11149                 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11150               std::map<int, int>::iterator itdom = domvol.begin();
11151               for (; itdom != domvol.end(); ++itdom)
11152                 {
11153                   int idom = itdom->first;
11154                   //MESSAGE("         domain " << idom);
11155                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11156                     {
11157                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11158                         {
11159                           vector<int> orderedDoms;
11160                           //MESSAGE("multiple node " << oldId);
11161                           isMultipleDetected =true;
11162                           if (mutipleNodes.count(oldId))
11163                             orderedDoms = mutipleNodes[oldId];
11164                           else
11165                             {
11166                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11167                               for (; it != nodeDomains[oldId].end(); ++it)
11168                                 orderedDoms.push_back(it->first);
11169                             }
11170                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11171                           //stringstream txt;
11172                           //for (int i=0; i<orderedDoms.size(); i++)
11173                           //  txt << orderedDoms[i] << " ";
11174                           //MESSAGE("orderedDoms " << txt.str());
11175                           mutipleNodes[oldId] = orderedDoms;
11176                         }
11177                       double *coords = grid->GetPoint(oldId);
11178                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11179                       int newId = newNode->getVtkId();
11180                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11181                       //MESSAGE("   newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
11182                     }
11183                   if (nodeDomains[oldId].size() >= 3)
11184                     {
11185                       //MESSAGE("confirm multiple node " << oldId);
11186                       isMultipleDetected =true;
11187                     }
11188                 }
11189             }
11190           if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
11191             {
11192               //MESSAGE("multiple Nodes detected on a shared face");
11193               int downId = itface->first.cellId;
11194               unsigned char cellType = itface->first.cellType;
11195               // --- shared edge or shared face ?
11196               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11197                 {
11198                   int nodes[3];
11199                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11200                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11201                     if (mutipleNodes.count(nodes[i]))
11202                       if (!mutipleNodesToFace.count(nodes[i]))
11203                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11204                }
11205               else // shared face (between two volumes)
11206                 {
11207                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11208                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11209                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11210                   for (int ie =0; ie < nbEdges; ie++)
11211                     {
11212                       int nodes[3];
11213                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11214                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11215                         {
11216                           vector<int> vn0 = mutipleNodes[nodes[0]];
11217                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11218                           sort( vn0.begin(), vn0.end() );
11219                           sort( vn1.begin(), vn1.end() );
11220                           if (vn0 == vn1)
11221                             {
11222                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11223                               double *coords = grid->GetPoint(nodes[0]);
11224                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11225                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11226                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11227                               gp_Pnt gref;
11228                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11229                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11230                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11231                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11232                               for (int id=0; id < vn0.size(); id++)
11233                                 {
11234                                   int idom = vn0[id];
11235                                   for (int ivol=0; ivol<nbvol; ivol++)
11236                                     {
11237                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11238                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11239                                       if (theElems[idom].count(elem))
11240                                         {
11241                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11242                                           domvol[idom] = svol;
11243                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11244                                           double values[3];
11245                                           vtkIdType npts = 0;
11246                                           vtkIdType* pts = 0;
11247                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11248                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11249                                           if (id ==0)
11250                                             {
11251                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11252                                               angleDom[idom] = 0;
11253                                             }
11254                                           else
11255                                             {
11256                                               gp_Pnt g(values[0], values[1], values[2]);
11257                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11258                                               //MESSAGE("  angle=" << angleDom[idom]);
11259                                             }
11260                                           break;
11261                                         }
11262                                     }
11263                                 }
11264                               map<double, int> sortedDom; // sort domains by angle
11265                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11266                                 sortedDom[ia->second] = ia->first;
11267                               vector<int> vnodes;
11268                               vector<int> vdom;
11269                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11270                                 {
11271                                   vdom.push_back(ib->second);
11272                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11273                                 }
11274                               for (int ino = 0; ino < nbNodes; ino++)
11275                                 vnodes.push_back(nodes[ino]);
11276                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11277                             }
11278                         }
11279                     }
11280                 }
11281             }
11282         }
11283     }
11284
11285   // --- iterate on shared faces (volumes to modify, face to extrude)
11286   //     get node id's of the face (id SMDS = id VTK)
11287   //     create flat element with old and new nodes if requested
11288
11289   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11290   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11291
11292   std::map<int, std::map<long,int> > nodeQuadDomains;
11293   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11294
11295   if (createJointElems)
11296     {
11297       itface = faceDomains.begin();
11298       for (; itface != faceDomains.end(); ++itface)
11299         {
11300           DownIdType face = itface->first;
11301           std::set<int> oldNodes;
11302           std::set<int>::iterator itn;
11303           oldNodes.clear();
11304           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11305
11306           std::map<int, int> domvol = itface->second;
11307           std::map<int, int>::iterator itdom = domvol.begin();
11308           int dom1 = itdom->first;
11309           int vtkVolId = itdom->second;
11310           itdom++;
11311           int dom2 = itdom->first;
11312           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11313                                                              nodeQuadDomains);
11314           stringstream grpname;
11315           grpname << "j_";
11316           if (dom1 < dom2)
11317             grpname << dom1 << "_" << dom2;
11318           else
11319             grpname << dom2 << "_" << dom1;
11320           int idg;
11321           string namegrp = grpname.str();
11322           if (!mapOfJunctionGroups.count(namegrp))
11323             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11324           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11325           if (sgrp)
11326             sgrp->Add(vol->GetID());
11327         }
11328     }
11329
11330   // --- create volumes on multiple domain intersection if requested
11331   //     iterate on mutipleNodesToFace
11332   //     iterate on edgesMultiDomains
11333
11334   if (createJointElems)
11335     {
11336       // --- iterate on mutipleNodesToFace
11337
11338       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11339       for (; itn != mutipleNodesToFace.end(); ++itn)
11340         {
11341           int node = itn->first;
11342           vector<int> orderDom = itn->second;
11343           vector<vtkIdType> orderedNodes;
11344           for (int idom = 0; idom <orderDom.size(); idom++)
11345             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11346             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11347
11348             stringstream grpname;
11349             grpname << "m2j_";
11350             grpname << 0 << "_" << 0;
11351             int idg;
11352             string namegrp = grpname.str();
11353             if (!mapOfJunctionGroups.count(namegrp))
11354               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11355             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11356             if (sgrp)
11357               sgrp->Add(face->GetID());
11358         }
11359
11360       // --- iterate on edgesMultiDomains
11361
11362       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11363       for (; ite != edgesMultiDomains.end(); ++ite)
11364         {
11365           vector<int> nodes = ite->first;
11366           vector<int> orderDom = ite->second;
11367           vector<vtkIdType> orderedNodes;
11368           if (nodes.size() == 2)
11369             {
11370               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11371               for (int ino=0; ino < nodes.size(); ino++)
11372                 if (orderDom.size() == 3)
11373                   for (int idom = 0; idom <orderDom.size(); idom++)
11374                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11375                 else
11376                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11377                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11378               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11379
11380               stringstream grpname;
11381               grpname << "mj_";
11382               grpname << 0 << "_" << 0;
11383               int idg;
11384               string namegrp = grpname.str();
11385               if (!mapOfJunctionGroups.count(namegrp))
11386                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11387               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11388               if (sgrp)
11389                 sgrp->Add(vol->GetID());
11390             }
11391           else
11392             {
11393               MESSAGE("Quadratic multiple joints not implemented");
11394               // TODO quadratic nodes
11395             }
11396         }
11397     }
11398
11399   // --- list the explicit faces and edges of the mesh that need to be modified,
11400   //     i.e. faces and edges built with one or more duplicated nodes.
11401   //     associate these faces or edges to their corresponding domain.
11402   //     only the first domain found is kept when a face or edge is shared
11403
11404   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11405   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11406   faceOrEdgeDom.clear();
11407   feDom.clear();
11408
11409   for (int idomain = 0; idomain < theElems.size(); idomain++)
11410     {
11411       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11412       for (; itnod != nodeDomains.end(); ++itnod)
11413         {
11414           int oldId = itnod->first;
11415           //MESSAGE("     node " << oldId);
11416           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11417           for (int i = 0; i < l.ncells; i++)
11418             {
11419               int vtkId = l.cells[i];
11420               int vtkType = grid->GetCellType(vtkId);
11421               int downId = grid->CellIdToDownId(vtkId);
11422               if (downId < 0)
11423                 continue; // new cells: not to be modified
11424               DownIdType aCell(downId, vtkType);
11425               int volParents[1000];
11426               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11427               for (int j = 0; j < nbvol; j++)
11428                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11429                   if (!feDom.count(vtkId))
11430                     {
11431                       feDom[vtkId] = idomain;
11432                       faceOrEdgeDom[aCell] = emptyMap;
11433                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11434                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11435                       //        << " type " << vtkType << " downId " << downId);
11436                     }
11437             }
11438         }
11439     }
11440
11441   // --- iterate on shared faces (volumes to modify, face to extrude)
11442   //     get node id's of the face
11443   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11444
11445   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11446   for (int m=0; m<3; m++)
11447     {
11448       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11449       itface = (*amap).begin();
11450       for (; itface != (*amap).end(); ++itface)
11451         {
11452           DownIdType face = itface->first;
11453           std::set<int> oldNodes;
11454           std::set<int>::iterator itn;
11455           oldNodes.clear();
11456           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11457           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11458           std::map<int, int> localClonedNodeIds;
11459
11460           std::map<int, int> domvol = itface->second;
11461           std::map<int, int>::iterator itdom = domvol.begin();
11462           for (; itdom != domvol.end(); ++itdom)
11463             {
11464               int idom = itdom->first;
11465               int vtkVolId = itdom->second;
11466               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11467               localClonedNodeIds.clear();
11468               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11469                 {
11470                   int oldId = *itn;
11471                   if (nodeDomains[oldId].count(idom))
11472                     {
11473                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11474                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11475                     }
11476                 }
11477               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11478             }
11479         }
11480     }
11481
11482   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11483   grid->BuildLinks();
11484
11485   CHRONOSTOP(50);
11486   counters::stats();
11487   return true;
11488 }
11489
11490 /*!
11491  * \brief Double nodes on some external faces and create flat elements.
11492  * Flat elements are mainly used by some types of mechanic calculations.
11493  *
11494  * Each group of the list must be constituted of faces.
11495  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11496  * @param theElems - list of groups of faces, where a group of faces is a set of
11497  * SMDS_MeshElements sorted by Id.
11498  * @return TRUE if operation has been completed successfully, FALSE otherwise
11499  */
11500 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11501 {
11502   MESSAGE("-------------------------------------------------");
11503   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11504   MESSAGE("-------------------------------------------------");
11505
11506   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11507
11508   // --- For each group of faces
11509   //     duplicate the nodes, create a flat element based on the face
11510   //     replace the nodes of the faces by their clones
11511
11512   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11513   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11514   clonedNodes.clear();
11515   intermediateNodes.clear();
11516   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11517   mapOfJunctionGroups.clear();
11518
11519   for (int idom = 0; idom < theElems.size(); idom++)
11520     {
11521       const TIDSortedElemSet& domain = theElems[idom];
11522       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11523       for (; elemItr != domain.end(); ++elemItr)
11524         {
11525           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11526           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11527           if (!aFace)
11528             continue;
11529           // MESSAGE("aFace=" << aFace->GetID());
11530           bool isQuad = aFace->IsQuadratic();
11531           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11532
11533           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11534
11535           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11536           while (nodeIt->more())
11537             {
11538               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11539               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11540               if (isMedium)
11541                 ln2.push_back(node);
11542               else
11543                 ln0.push_back(node);
11544
11545               const SMDS_MeshNode* clone = 0;
11546               if (!clonedNodes.count(node))
11547                 {
11548                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11549                   clonedNodes[node] = clone;
11550                 }
11551               else
11552                 clone = clonedNodes[node];
11553
11554               if (isMedium)
11555                 ln3.push_back(clone);
11556               else
11557                 ln1.push_back(clone);
11558
11559               const SMDS_MeshNode* inter = 0;
11560               if (isQuad && (!isMedium))
11561                 {
11562                   if (!intermediateNodes.count(node))
11563                     {
11564                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11565                       intermediateNodes[node] = inter;
11566                     }
11567                   else
11568                     inter = intermediateNodes[node];
11569                   ln4.push_back(inter);
11570                 }
11571             }
11572
11573           // --- extrude the face
11574
11575           vector<const SMDS_MeshNode*> ln;
11576           SMDS_MeshVolume* vol = 0;
11577           vtkIdType aType = aFace->GetVtkType();
11578           switch (aType)
11579           {
11580             case VTK_TRIANGLE:
11581               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11582               // MESSAGE("vol prism " << vol->GetID());
11583               ln.push_back(ln1[0]);
11584               ln.push_back(ln1[1]);
11585               ln.push_back(ln1[2]);
11586               break;
11587             case VTK_QUAD:
11588               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11589               // MESSAGE("vol hexa " << vol->GetID());
11590               ln.push_back(ln1[0]);
11591               ln.push_back(ln1[1]);
11592               ln.push_back(ln1[2]);
11593               ln.push_back(ln1[3]);
11594               break;
11595             case VTK_QUADRATIC_TRIANGLE:
11596               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11597                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11598               // MESSAGE("vol quad prism " << vol->GetID());
11599               ln.push_back(ln1[0]);
11600               ln.push_back(ln1[1]);
11601               ln.push_back(ln1[2]);
11602               ln.push_back(ln3[0]);
11603               ln.push_back(ln3[1]);
11604               ln.push_back(ln3[2]);
11605               break;
11606             case VTK_QUADRATIC_QUAD:
11607 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11608 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11609 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11610               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11611                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11612                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11613               // MESSAGE("vol quad hexa " << vol->GetID());
11614               ln.push_back(ln1[0]);
11615               ln.push_back(ln1[1]);
11616               ln.push_back(ln1[2]);
11617               ln.push_back(ln1[3]);
11618               ln.push_back(ln3[0]);
11619               ln.push_back(ln3[1]);
11620               ln.push_back(ln3[2]);
11621               ln.push_back(ln3[3]);
11622               break;
11623             case VTK_POLYGON:
11624               break;
11625             default:
11626               break;
11627           }
11628
11629           if (vol)
11630             {
11631               stringstream grpname;
11632               grpname << "jf_";
11633               grpname << idom;
11634               int idg;
11635               string namegrp = grpname.str();
11636               if (!mapOfJunctionGroups.count(namegrp))
11637                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11638               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11639               if (sgrp)
11640                 sgrp->Add(vol->GetID());
11641             }
11642
11643           // --- modify the face
11644
11645           aFace->ChangeNodes(&ln[0], ln.size());
11646         }
11647     }
11648   return true;
11649 }
11650
11651 //================================================================================
11652 /*!
11653  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11654  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11655  * \return TRUE if operation has been completed successfully, FALSE otherwise
11656  */
11657 //================================================================================
11658
11659 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11660 {
11661   // iterates on volume elements and detect all free faces on them
11662   SMESHDS_Mesh* aMesh = GetMeshDS();
11663   if (!aMesh)
11664     return false;
11665   //bool res = false;
11666   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11667   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11668   while(vIt->more())
11669   {
11670     const SMDS_MeshVolume* volume = vIt->next();
11671     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11672     vTool.SetExternalNormal();
11673     //const bool isPoly = volume->IsPoly();
11674     const int iQuad = volume->IsQuadratic();
11675     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11676     {
11677       if (!vTool.IsFreeFace(iface))
11678         continue;
11679       nbFree++;
11680       vector<const SMDS_MeshNode *> nodes;
11681       int nbFaceNodes = vTool.NbFaceNodes(iface);
11682       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11683       int inode = 0;
11684       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11685         nodes.push_back(faceNodes[inode]);
11686       if (iQuad) { // add medium nodes
11687         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11688           nodes.push_back(faceNodes[inode]);
11689         if ( nbFaceNodes == 9 ) // bi-quadratic quad
11690           nodes.push_back(faceNodes[8]);
11691       }
11692       // add new face based on volume nodes
11693       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11694         nbExisted++;
11695         continue; // face already exsist
11696       }
11697       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11698       nbCreated++;
11699     }
11700   }
11701   return ( nbFree==(nbExisted+nbCreated) );
11702 }
11703
11704 namespace
11705 {
11706   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11707   {
11708     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11709       return n;
11710     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11711   }
11712 }
11713 //================================================================================
11714 /*!
11715  * \brief Creates missing boundary elements
11716  *  \param elements - elements whose boundary is to be checked
11717  *  \param dimension - defines type of boundary elements to create
11718  *  \param group - a group to store created boundary elements in
11719  *  \param targetMesh - a mesh to store created boundary elements in
11720  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11721  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
11722  *                                boundary elements will be copied into the targetMesh
11723  *  \param toAddExistingBondary - if true, not only new but also pre-existing
11724  *                                boundary elements will be added into the new group
11725  *  \param aroundElements - if true, elements will be created on boundary of given
11726  *                          elements else, on boundary of the whole mesh.
11727  * \return nb of added boundary elements
11728  */
11729 //================================================================================
11730
11731 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11732                                        Bnd_Dimension           dimension,
11733                                        SMESH_Group*            group/*=0*/,
11734                                        SMESH_Mesh*             targetMesh/*=0*/,
11735                                        bool                    toCopyElements/*=false*/,
11736                                        bool                    toCopyExistingBoundary/*=false*/,
11737                                        bool                    toAddExistingBondary/*= false*/,
11738                                        bool                    aroundElements/*= false*/)
11739 {
11740   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11741   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11742   // hope that all elements are of the same type, do not check them all
11743   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11744     throw SALOME_Exception(LOCALIZED("wrong element type"));
11745
11746   if ( !targetMesh )
11747     toCopyElements = toCopyExistingBoundary = false;
11748
11749   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11750   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11751   int nbAddedBnd = 0;
11752
11753   // editor adding present bnd elements and optionally holding elements to add to the group
11754   SMESH_MeshEditor* presentEditor;
11755   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11756   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11757
11758   SMESH_MesherHelper helper( *myMesh );
11759   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11760   SMDS_VolumeTool vTool;
11761   TIDSortedElemSet avoidSet;
11762   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11763   int inode;
11764
11765   typedef vector<const SMDS_MeshNode*> TConnectivity;
11766
11767   SMDS_ElemIteratorPtr eIt;
11768   if (elements.empty())
11769     eIt = aMesh->elementsIterator(elemType);
11770   else
11771     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11772
11773   while (eIt->more())
11774   {
11775     const SMDS_MeshElement* elem = eIt->next();
11776     const int iQuad = elem->IsQuadratic();
11777
11778     // ------------------------------------------------------------------------------------
11779     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11780     // ------------------------------------------------------------------------------------
11781     vector<const SMDS_MeshElement*> presentBndElems;
11782     vector<TConnectivity>           missingBndElems;
11783     TConnectivity nodes;
11784     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11785     {
11786       vTool.SetExternalNormal();
11787       const SMDS_MeshElement* otherVol = 0;
11788       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11789       {
11790         if ( !vTool.IsFreeFace(iface, &otherVol) &&
11791              ( !aroundElements || elements.count( otherVol )))
11792           continue;
11793         const int nbFaceNodes = vTool.NbFaceNodes(iface);
11794         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11795         if ( missType == SMDSAbs_Edge ) // boundary edges
11796         {
11797           nodes.resize( 2+iQuad );
11798           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11799           {
11800             for ( int j = 0; j < nodes.size(); ++j )
11801               nodes[j] =nn[i+j];
11802             if ( const SMDS_MeshElement* edge =
11803                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11804               presentBndElems.push_back( edge );
11805             else
11806               missingBndElems.push_back( nodes );
11807           }
11808         }
11809         else // boundary face
11810         {
11811           nodes.clear();
11812           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11813             nodes.push_back( nn[inode] );
11814           if (iQuad) // add medium nodes
11815             for ( inode = 1; inode < nbFaceNodes; inode += 2)
11816               nodes.push_back( nn[inode] );
11817           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11818           if ( iCenter > 0 )
11819             nodes.push_back( vTool.GetNodes()[ iCenter ] );
11820
11821           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11822                                                                SMDSAbs_Face, /*noMedium=*/false ))
11823             presentBndElems.push_back( f );
11824           else
11825             missingBndElems.push_back( nodes );
11826
11827           if ( targetMesh != myMesh )
11828           {
11829             // add 1D elements on face boundary to be added to a new mesh
11830             const SMDS_MeshElement* edge;
11831             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11832             {
11833               if ( iQuad )
11834                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11835               else
11836                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11837               if ( edge && avoidSet.insert( edge ).second )
11838                 presentBndElems.push_back( edge );
11839             }
11840           }
11841         }
11842       }
11843     }
11844     else                     // elem is a face ------------------------------------------
11845     {
11846       avoidSet.clear(), avoidSet.insert( elem );
11847       int nbNodes = elem->NbCornerNodes();
11848       nodes.resize( 2 /*+ iQuad*/);
11849       for ( int i = 0; i < nbNodes; i++ )
11850       {
11851         nodes[0] = elem->GetNode(i);
11852         nodes[1] = elem->GetNode((i+1)%nbNodes);
11853         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11854           continue; // not free link
11855
11856         //if ( iQuad )
11857         //nodes[2] = elem->GetNode( i + nbNodes );
11858         if ( const SMDS_MeshElement* edge =
11859              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11860           presentBndElems.push_back( edge );
11861         else
11862           missingBndElems.push_back( nodes );
11863       }
11864     }
11865
11866     // ---------------------------------
11867     // 2. Add missing boundary elements
11868     // ---------------------------------
11869     if ( targetMesh != myMesh )
11870       // instead of making a map of nodes in this mesh and targetMesh,
11871       // we create nodes with same IDs.
11872       for ( int i = 0; i < missingBndElems.size(); ++i )
11873       {
11874         TConnectivity& srcNodes = missingBndElems[i];
11875         TConnectivity  nodes( srcNodes.size() );
11876         for ( inode = 0; inode < nodes.size(); ++inode )
11877           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11878         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11879                                                                    missType,
11880                                                                    /*noMedium=*/false))
11881           continue;
11882         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11883         ++nbAddedBnd;
11884       }
11885     else
11886       for ( int i = 0; i < missingBndElems.size(); ++i )
11887       {
11888         TConnectivity& nodes = missingBndElems[i];
11889         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11890                                                                    missType,
11891                                                                    /*noMedium=*/false))
11892           continue;
11893         SMDS_MeshElement* elem = 
11894           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11895         ++nbAddedBnd;
11896
11897         // try to set a new element to a shape
11898         if ( myMesh->HasShapeToMesh() )
11899         {
11900           bool ok = true;
11901           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11902           const int nbN = nodes.size() / (iQuad+1 );
11903           for ( inode = 0; inode < nbN && ok; ++inode )
11904           {
11905             pair<int, TopAbs_ShapeEnum> i_stype =
11906               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11907             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11908               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11909           }
11910           if ( ok && mediumShapes.size() > 1 )
11911           {
11912             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11913             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11914             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11915             {
11916               if (( ok = ( stype_i->first != stype_i_0.first )))
11917                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11918                                         aMesh->IndexToShape( stype_i_0.second ));
11919             }
11920           }
11921           if ( ok && mediumShapes.begin()->first == missShapeType )
11922             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11923         }
11924       }
11925
11926     // ----------------------------------
11927     // 3. Copy present boundary elements
11928     // ----------------------------------
11929     if ( toCopyExistingBoundary )
11930       for ( int i = 0 ; i < presentBndElems.size(); ++i )
11931       {
11932         const SMDS_MeshElement* e = presentBndElems[i];
11933         TConnectivity nodes( e->NbNodes() );
11934         for ( inode = 0; inode < nodes.size(); ++inode )
11935           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11936         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11937       }
11938     else // store present elements to add them to a group
11939       for ( int i = 0 ; i < presentBndElems.size(); ++i )
11940       {
11941         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11942       }
11943       
11944   } // loop on given elements
11945
11946   // ---------------------------------------------
11947   // 4. Fill group with boundary elements
11948   // ---------------------------------------------
11949   if ( group )
11950   {
11951     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11952       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11953         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11954   }
11955   tgtEditor.myLastCreatedElems.Clear();
11956   tgtEditor2.myLastCreatedElems.Clear();
11957
11958   // -----------------------
11959   // 5. Copy given elements
11960   // -----------------------
11961   if ( toCopyElements && targetMesh != myMesh )
11962   {
11963     if (elements.empty())
11964       eIt = aMesh->elementsIterator(elemType);
11965     else
11966       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11967     while (eIt->more())
11968     {
11969       const SMDS_MeshElement* elem = eIt->next();
11970       TConnectivity nodes( elem->NbNodes() );
11971       for ( inode = 0; inode < nodes.size(); ++inode )
11972         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11973       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11974
11975       tgtEditor.myLastCreatedElems.Clear();
11976     }
11977   }
11978   return nbAddedBnd;
11979 }