Salome HOME
a6a4d9b715680ca6d52685fa8e2f0df892e30f2f
[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 <boost/tuple/tuple.hpp>
101
102 #include <Standard_Failure.hxx>
103 #include <Standard_ErrorHandler.hxx>
104
105 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106
107 using namespace std;
108 using namespace SMESH::Controls;
109
110 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> >    TElemOfNodeListMap;
111 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
112
113 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
114
115 //=======================================================================
116 //function : SMESH_MeshEditor
117 //purpose  :
118 //=======================================================================
119
120 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
121   :myMesh( theMesh ) // theMesh may be NULL
122 {
123 }
124
125 //================================================================================
126 /*!
127  * \brief Clears myLastCreatedNodes and myLastCreatedElems
128  */
129 //================================================================================
130
131 void SMESH_MeshEditor::CrearLastCreated()
132 {
133   myLastCreatedNodes.Clear();
134   myLastCreatedElems.Clear();
135 }
136
137
138 //=======================================================================
139 /*!
140  * \brief Add element
141  */
142 //=======================================================================
143
144 SMDS_MeshElement*
145 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
146                              const SMDSAbs_ElementType            type,
147                              const bool                           isPoly,
148                              const int                            ID,
149                              const double                         ballDiameter)
150 {
151   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
152   SMDS_MeshElement* e = 0;
153   int nbnode = node.size();
154   SMESHDS_Mesh* mesh = GetMeshDS();
155   switch ( type ) {
156   case SMDSAbs_Face:
157     if ( !isPoly ) {
158       if      (nbnode == 3) {
159         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
160         else           e = mesh->AddFace      (node[0], node[1], node[2] );
161       }
162       else if (nbnode == 4) {
163         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
164         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
165       }
166       else if (nbnode == 6) {
167         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
168                                                node[4], node[5], ID);
169         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
170                                                node[4], node[5] );
171       }
172       else if (nbnode == 8) {
173         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
174                                                node[4], node[5], node[6], node[7], ID);
175         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
176                                                node[4], node[5], node[6], node[7] );
177       }
178       else if (nbnode == 9) {
179         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
180                                                node[4], node[5], node[6], node[7], node[8], ID);
181         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
182                                                node[4], node[5], node[6], node[7], node[8] );
183       }
184     } else {
185       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
186       else           e = mesh->AddPolygonalFace      (node    );
187     }
188     break;
189
190   case SMDSAbs_Volume:
191     if ( !isPoly ) {
192       if      (nbnode == 4) {
193         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
194         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
195       }
196       else if (nbnode == 5) {
197         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
198                                                  node[4], ID);
199         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
200                                                  node[4] );
201       }
202       else if (nbnode == 6) {
203         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
204                                                  node[4], node[5], ID);
205         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
206                                                  node[4], node[5] );
207       }
208       else if (nbnode == 8) {
209         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
210                                                  node[4], node[5], node[6], node[7], ID);
211         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], node[6], node[7] );
213       }
214       else if (nbnode == 10) {
215         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
216                                                  node[4], node[5], node[6], node[7],
217                                                  node[8], node[9], ID);
218         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
219                                                  node[4], node[5], node[6], node[7],
220                                                  node[8], node[9] );
221       }
222       else if (nbnode == 12) {
223         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224                                                  node[4], node[5], node[6], node[7],
225                                                  node[8], node[9], node[10], node[11], ID);
226         else           e = mesh->AddVolume      (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       }
230       else if (nbnode == 13) {
231         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232                                                  node[4], node[5], node[6], node[7],
233                                                  node[8], node[9], node[10],node[11],
234                                                  node[12],ID);
235         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
236                                                  node[4], node[5], node[6], node[7],
237                                                  node[8], node[9], node[10],node[11],
238                                                  node[12] );
239       }
240       else if (nbnode == 15) {
241         if ( ID >= 1 ) e = mesh->AddVolumeWithID(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],ID);
245         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
246                                                  node[4], node[5], node[6], node[7],
247                                                  node[8], node[9], node[10],node[11],
248                                                  node[12],node[13],node[14] );
249       }
250       else if (nbnode == 20) {
251         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252                                                  node[4], node[5], node[6], node[7],
253                                                  node[8], node[9], node[10],node[11],
254                                                  node[12],node[13],node[14],node[15],
255                                                  node[16],node[17],node[18],node[19],ID);
256         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
257                                                  node[4], node[5], node[6], node[7],
258                                                  node[8], node[9], node[10],node[11],
259                                                  node[12],node[13],node[14],node[15],
260                                                  node[16],node[17],node[18],node[19] );
261       }
262       else if (nbnode == 27) {
263         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264                                                  node[4], node[5], node[6], node[7],
265                                                  node[8], node[9], node[10],node[11],
266                                                  node[12],node[13],node[14],node[15],
267                                                  node[16],node[17],node[18],node[19],
268                                                  node[20],node[21],node[22],node[23],
269                                                  node[24],node[25],node[26], ID);
270         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
271                                                  node[4], node[5], node[6], node[7],
272                                                  node[8], node[9], node[10],node[11],
273                                                  node[12],node[13],node[14],node[15],
274                                                  node[16],node[17],node[18],node[19],
275                                                  node[20],node[21],node[22],node[23],
276                                                  node[24],node[25],node[26] );
277       }
278     }
279     break;
280
281   case SMDSAbs_Edge:
282     if ( nbnode == 2 ) {
283       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
284       else           e = mesh->AddEdge      (node[0], node[1] );
285     }
286     else if ( nbnode == 3 ) {
287       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
288       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
289     }
290     break;
291
292   case SMDSAbs_0DElement:
293     if ( nbnode == 1 ) {
294       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
295       else           e = mesh->Add0DElement      (node[0] );
296     }
297     break;
298
299   case SMDSAbs_Node:
300     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
301     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
302     break;
303
304   case SMDSAbs_Ball:
305     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
306     else           e = mesh->AddBall      (node[0], ballDiameter);
307     break;
308
309   default:;
310   }
311   if ( e ) myLastCreatedElems.Append( e );
312   return e;
313 }
314
315 //=======================================================================
316 /*!
317  * \brief Add element
318  */
319 //=======================================================================
320
321 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
322                                                const SMDSAbs_ElementType type,
323                                                const bool                isPoly,
324                                                const int                 ID)
325 {
326   vector<const SMDS_MeshNode*> nodes;
327   nodes.reserve( nodeIDs.size() );
328   vector<int>::const_iterator id = nodeIDs.begin();
329   while ( id != nodeIDs.end() ) {
330     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
331       nodes.push_back( node );
332     else
333       return 0;
334   }
335   return AddElement( nodes, type, isPoly, ID );
336 }
337
338 //=======================================================================
339 //function : Remove
340 //purpose  : Remove a node or an element.
341 //           Modify a compute state of sub-meshes which become empty
342 //=======================================================================
343
344 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
345                               const bool         isNodes )
346 {
347   myLastCreatedElems.Clear();
348   myLastCreatedNodes.Clear();
349
350   SMESHDS_Mesh* aMesh = GetMeshDS();
351   set< SMESH_subMesh *> smmap;
352
353   int removed = 0;
354   list<int>::const_iterator it = theIDs.begin();
355   for ( ; it != theIDs.end(); it++ ) {
356     const SMDS_MeshElement * elem;
357     if ( isNodes )
358       elem = aMesh->FindNode( *it );
359     else
360       elem = aMesh->FindElement( *it );
361     if ( !elem )
362       continue;
363
364     // Notify VERTEX sub-meshes about modification
365     if ( isNodes ) {
366       const SMDS_MeshNode* node = cast2Node( elem );
367       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
368         if ( int aShapeID = node->getshapeId() )
369           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
370             smmap.insert( sm );
371     }
372     // Find sub-meshes to notify about modification
373     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
374     //     while ( nodeIt->more() ) {
375     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
376     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
377     //       if ( aPosition.get() ) {
378     //         if ( int aShapeID = aPosition->GetShapeId() ) {
379     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
380     //             smmap.insert( sm );
381     //         }
382     //       }
383     //     }
384
385     // Do remove
386     if ( isNodes )
387       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
388     else
389       aMesh->RemoveElement( elem );
390     removed++;
391   }
392
393   // Notify sub-meshes about modification
394   if ( !smmap.empty() ) {
395     set< SMESH_subMesh *>::iterator smIt;
396     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
397       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
398   }
399
400   //   // Check if the whole mesh becomes empty
401   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
402   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
403
404   return removed;
405 }
406
407 //================================================================================
408 /*!
409  * \brief Create 0D elements on all nodes of the given object except those
410  *        nodes on which a 0D element already exists.
411  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
412  *                    the all mesh is treated
413  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
414  */
415 //================================================================================
416
417 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
418                                                    TIDSortedElemSet&       all0DElems )
419 {
420   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
421   SMDS_ElemIteratorPtr elemIt;
422   if ( elements.empty() )
423     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
424   else
425     elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
426
427   while ( elemIt->more() )
428   {
429     const SMDS_MeshElement* e = elemIt->next();
430     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
431     while ( nodeIt->more() )
432     {
433       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
434       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
435       if ( it0D->more() )
436         all0DElems.insert( it0D->next() );
437       else {
438         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
439         all0DElems.insert( myLastCreatedElems.Last() );
440       }
441     }
442   }
443 }
444
445 //=======================================================================
446 //function : FindShape
447 //purpose  : Return an index of the shape theElem is on
448 //           or zero if a shape not found
449 //=======================================================================
450
451 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
452 {
453   myLastCreatedElems.Clear();
454   myLastCreatedNodes.Clear();
455
456   SMESHDS_Mesh * aMesh = GetMeshDS();
457   if ( aMesh->ShapeToMesh().IsNull() )
458     return 0;
459
460   int aShapeID = theElem->getshapeId();
461   if ( aShapeID < 1 )
462     return 0;
463
464   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
465     if ( sm->Contains( theElem ))
466       return aShapeID;
467
468   if ( theElem->GetType() == SMDSAbs_Node ) {
469     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
470   }
471   else {
472     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
473   }
474
475   TopoDS_Shape aShape; // the shape a node of theElem is on
476   if ( theElem->GetType() != SMDSAbs_Node )
477   {
478     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
479     while ( nodeIt->more() ) {
480       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
481       if ((aShapeID = node->getshapeId()) > 0) {
482         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
483           if ( sm->Contains( theElem ))
484             return aShapeID;
485           if ( aShape.IsNull() )
486             aShape = aMesh->IndexToShape( aShapeID );
487         }
488       }
489     }
490   }
491
492   // None of nodes is on a proper shape,
493   // find the shape among ancestors of aShape on which a node is
494   if ( !aShape.IsNull() ) {
495     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
496     for ( ; ancIt.More(); ancIt.Next() ) {
497       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
498       if ( sm && sm->Contains( theElem ))
499         return aMesh->ShapeToIndex( ancIt.Value() );
500     }
501   }
502   else
503   {
504     const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
505     map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
506     for ( ; id_sm != id2sm.end(); ++id_sm )
507       if ( id_sm->second->Contains( theElem ))
508         return id_sm->first;
509   }
510
511   //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
512   return 0;
513 }
514
515 //=======================================================================
516 //function : IsMedium
517 //purpose  :
518 //=======================================================================
519
520 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
521                                 const SMDSAbs_ElementType typeToCheck)
522 {
523   bool isMedium = false;
524   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
525   while (it->more() && !isMedium ) {
526     const SMDS_MeshElement* elem = it->next();
527     isMedium = elem->IsMediumNode(node);
528   }
529   return isMedium;
530 }
531
532 //=======================================================================
533 //function : ShiftNodesQuadTria
534 //purpose  : auxilary
535 //           Shift nodes in the array corresponded to quadratic triangle
536 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
537 //=======================================================================
538 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
539 {
540   const SMDS_MeshNode* nd1 = aNodes[0];
541   aNodes[0] = aNodes[1];
542   aNodes[1] = aNodes[2];
543   aNodes[2] = nd1;
544   const SMDS_MeshNode* nd2 = aNodes[3];
545   aNodes[3] = aNodes[4];
546   aNodes[4] = aNodes[5];
547   aNodes[5] = nd2;
548 }
549
550 //=======================================================================
551 //function : edgeConnectivity
552 //purpose  : auxilary
553 //           return number of the edges connected with the theNode.
554 //           if theEdges has connections with the other type of the
555 //           elements, return -1
556 //=======================================================================
557 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
558 {
559   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
560   int nb=0;
561   while(elemIt->more()) {
562     elemIt->next();
563     nb++;
564   }
565   return nb;
566 }
567
568
569 //=======================================================================
570 //function : GetNodesFromTwoTria
571 //purpose  : auxilary
572 //           Shift nodes in the array corresponded to quadratic triangle
573 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
574 //=======================================================================
575 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
576                                 const SMDS_MeshElement * theTria2,
577                                 const SMDS_MeshNode* N1[],
578                                 const SMDS_MeshNode* N2[])
579 {
580   SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
581   int i=0;
582   while(i<6) {
583     N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
584     i++;
585   }
586   if(it->more()) return false;
587   it = theTria2->nodesIterator();
588   i=0;
589   while(i<6) {
590     N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
591     i++;
592   }
593   if(it->more()) return false;
594
595   int sames[3] = {-1,-1,-1};
596   int nbsames = 0;
597   int j;
598   for(i=0; i<3; i++) {
599     for(j=0; j<3; j++) {
600       if(N1[i]==N2[j]) {
601         sames[i] = j;
602         nbsames++;
603         break;
604       }
605     }
606   }
607   if(nbsames!=2) return false;
608   if(sames[0]>-1) {
609     ShiftNodesQuadTria(N1);
610     if(sames[1]>-1) {
611       ShiftNodesQuadTria(N1);
612     }
613   }
614   i = sames[0] + sames[1] + sames[2];
615   for(; i<2; i++) {
616     ShiftNodesQuadTria(N2);
617   }
618   // now we receive following N1 and N2 (using numeration as above image)
619   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
620   // i.e. first nodes from both arrays determ new diagonal
621   return true;
622 }
623
624 //=======================================================================
625 //function : InverseDiag
626 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
627 //           but having other common link.
628 //           Return False if args are improper
629 //=======================================================================
630
631 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
632                                     const SMDS_MeshElement * theTria2 )
633 {
634   MESSAGE("InverseDiag");
635   myLastCreatedElems.Clear();
636   myLastCreatedNodes.Clear();
637
638   if (!theTria1 || !theTria2)
639     return false;
640
641   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
642   if (!F1) return false;
643   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
644   if (!F2) return false;
645   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
646       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
647
648     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
649     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
650     //    |/ |                                         | \|
651     //  B +--+ 2                                     B +--+ 2
652
653     // put nodes in array and find out indices of the same ones
654     const SMDS_MeshNode* aNodes [6];
655     int sameInd [] = { 0, 0, 0, 0, 0, 0 };
656     int i = 0;
657     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
658     while ( it->more() ) {
659       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
660
661       if ( i > 2 ) // theTria2
662         // find same node of theTria1
663         for ( int j = 0; j < 3; j++ )
664           if ( aNodes[ i ] == aNodes[ j ]) {
665             sameInd[ j ] = i;
666             sameInd[ i ] = j;
667             break;
668           }
669       // next
670       i++;
671       if ( i == 3 ) {
672         if ( it->more() )
673           return false; // theTria1 is not a triangle
674         it = theTria2->nodesIterator();
675       }
676       if ( i == 6 && it->more() )
677         return false; // theTria2 is not a triangle
678     }
679
680     // find indices of 1,2 and of A,B in theTria1
681     int iA = 0, iB = 0, i1 = 0, i2 = 0;
682     for ( i = 0; i < 6; i++ ) {
683       if ( sameInd [ i ] == 0 ) {
684         if ( i < 3 ) i1 = i;
685         else         i2 = i;
686       }
687       else if (i < 3) {
688         if ( iA ) iB = i;
689         else      iA = i;
690       }
691     }
692     // nodes 1 and 2 should not be the same
693     if ( aNodes[ i1 ] == aNodes[ i2 ] )
694       return false;
695
696     // theTria1: A->2
697     aNodes[ iA ] = aNodes[ i2 ];
698     // theTria2: B->1
699     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
700
701     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
702     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
703
704     return true;
705
706   } // end if(F1 && F2)
707
708   // check case of quadratic faces
709   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
710     return false;
711   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
712     return false;
713
714   //       5
715   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
716   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
717   //    |   / |
718   //  7 +  +  + 6
719   //    | /9  |
720   //    |/    |
721   //  4 +--+--+ 3
722   //       8
723
724   const SMDS_MeshNode* N1 [6];
725   const SMDS_MeshNode* N2 [6];
726   if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
727     return false;
728   // now we receive following N1 and N2 (using numeration as above image)
729   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
730   // i.e. first nodes from both arrays determ new diagonal
731
732   const SMDS_MeshNode* N1new [6];
733   const SMDS_MeshNode* N2new [6];
734   N1new[0] = N1[0];
735   N1new[1] = N2[0];
736   N1new[2] = N2[1];
737   N1new[3] = N1[4];
738   N1new[4] = N2[3];
739   N1new[5] = N1[5];
740   N2new[0] = N1[0];
741   N2new[1] = N1[1];
742   N2new[2] = N2[0];
743   N2new[3] = N1[3];
744   N2new[4] = N2[5];
745   N2new[5] = N1[4];
746   // replaces nodes in faces
747   GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
748   GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
749
750   return true;
751 }
752
753 //=======================================================================
754 //function : findTriangles
755 //purpose  : find triangles sharing theNode1-theNode2 link
756 //=======================================================================
757
758 static bool findTriangles(const SMDS_MeshNode *    theNode1,
759                           const SMDS_MeshNode *    theNode2,
760                           const SMDS_MeshElement*& theTria1,
761                           const SMDS_MeshElement*& theTria2)
762 {
763   if ( !theNode1 || !theNode2 ) return false;
764
765   theTria1 = theTria2 = 0;
766
767   set< const SMDS_MeshElement* > emap;
768   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
769   while (it->more()) {
770     const SMDS_MeshElement* elem = it->next();
771     if ( elem->NbNodes() == 3 )
772       emap.insert( elem );
773   }
774   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
775   while (it->more()) {
776     const SMDS_MeshElement* elem = it->next();
777     if ( emap.find( elem ) != emap.end() ) {
778       if ( theTria1 ) {
779         // theTria1 must be element with minimum ID
780         if( theTria1->GetID() < elem->GetID() ) {
781           theTria2 = elem;
782         }
783         else {
784           theTria2 = theTria1;
785           theTria1 = elem;
786         }
787         break;
788       }
789       else {
790         theTria1 = elem;
791       }
792     }
793   }
794   return ( theTria1 && theTria2 );
795 }
796
797 //=======================================================================
798 //function : InverseDiag
799 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
800 //           with ones built on the same 4 nodes but having other common link.
801 //           Return false if proper faces not found
802 //=======================================================================
803
804 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
805                                     const SMDS_MeshNode * theNode2)
806 {
807   myLastCreatedElems.Clear();
808   myLastCreatedNodes.Clear();
809
810   MESSAGE( "::InverseDiag()" );
811
812   const SMDS_MeshElement *tr1, *tr2;
813   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
814     return false;
815
816   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
817   if (!F1) return false;
818   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
819   if (!F2) return false;
820   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
821       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
822
823     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
824     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
825     //    |/ |                                    | \|
826     //  B +--+ 2                                B +--+ 2
827
828     // put nodes in array
829     // and find indices of 1,2 and of A in tr1 and of B in tr2
830     int i, iA1 = 0, i1 = 0;
831     const SMDS_MeshNode* aNodes1 [3];
832     SMDS_ElemIteratorPtr it;
833     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
834       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
835       if ( aNodes1[ i ] == theNode1 )
836         iA1 = i; // node A in tr1
837       else if ( aNodes1[ i ] != theNode2 )
838         i1 = i;  // node 1
839     }
840     int iB2 = 0, i2 = 0;
841     const SMDS_MeshNode* aNodes2 [3];
842     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
843       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
844       if ( aNodes2[ i ] == theNode2 )
845         iB2 = i; // node B in tr2
846       else if ( aNodes2[ i ] != theNode1 )
847         i2 = i;  // node 2
848     }
849
850     // nodes 1 and 2 should not be the same
851     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
852       return false;
853
854     // tr1: A->2
855     aNodes1[ iA1 ] = aNodes2[ i2 ];
856     // tr2: B->1
857     aNodes2[ iB2 ] = aNodes1[ i1 ];
858
859     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
860     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
861
862     return true;
863   }
864
865   // check case of quadratic faces
866   return InverseDiag(tr1,tr2);
867 }
868
869 //=======================================================================
870 //function : getQuadrangleNodes
871 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
872 //           fusion of triangles tr1 and tr2 having shared link on
873 //           theNode1 and theNode2
874 //=======================================================================
875
876 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
877                         const SMDS_MeshNode *    theNode1,
878                         const SMDS_MeshNode *    theNode2,
879                         const SMDS_MeshElement * tr1,
880                         const SMDS_MeshElement * tr2 )
881 {
882   if( tr1->NbNodes() != tr2->NbNodes() )
883     return false;
884   // find the 4-th node to insert into tr1
885   const SMDS_MeshNode* n4 = 0;
886   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
887   int i=0;
888   while ( !n4 && i<3 ) {
889     const SMDS_MeshNode * n = cast2Node( it->next() );
890     i++;
891     bool isDiag = ( n == theNode1 || n == theNode2 );
892     if ( !isDiag )
893       n4 = n;
894   }
895   // Make an array of nodes to be in a quadrangle
896   int iNode = 0, iFirstDiag = -1;
897   it = tr1->nodesIterator();
898   i=0;
899   while ( i<3 ) {
900     const SMDS_MeshNode * n = cast2Node( it->next() );
901     i++;
902     bool isDiag = ( n == theNode1 || n == theNode2 );
903     if ( isDiag ) {
904       if ( iFirstDiag < 0 )
905         iFirstDiag = iNode;
906       else if ( iNode - iFirstDiag == 1 )
907         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
908     }
909     else if ( n == n4 ) {
910       return false; // tr1 and tr2 should not have all the same nodes
911     }
912     theQuadNodes[ iNode++ ] = n;
913   }
914   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
915     theQuadNodes[ iNode ] = n4;
916
917   return true;
918 }
919
920 //=======================================================================
921 //function : DeleteDiag
922 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
923 //           with a quadrangle built on the same 4 nodes.
924 //           Return false if proper faces not found
925 //=======================================================================
926
927 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
928                                    const SMDS_MeshNode * theNode2)
929 {
930   myLastCreatedElems.Clear();
931   myLastCreatedNodes.Clear();
932
933   MESSAGE( "::DeleteDiag()" );
934
935   const SMDS_MeshElement *tr1, *tr2;
936   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
937     return false;
938
939   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
940   if (!F1) return false;
941   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
942   if (!F2) return false;
943   SMESHDS_Mesh * aMesh = GetMeshDS();
944
945   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
946       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
947
948     const SMDS_MeshNode* aNodes [ 4 ];
949     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
950       return false;
951
952     const SMDS_MeshElement* newElem = 0;
953     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
954     myLastCreatedElems.Append(newElem);
955     AddToSameGroups( newElem, tr1, aMesh );
956     int aShapeId = tr1->getshapeId();
957     if ( aShapeId )
958       {
959         aMesh->SetMeshElementOnShape( newElem, aShapeId );
960       }
961     aMesh->RemoveElement( tr1 );
962     aMesh->RemoveElement( tr2 );
963
964     return true;
965   }
966
967   // check case of quadratic faces
968   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
969     return false;
970   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
971     return false;
972
973   //       5
974   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
975   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
976   //    |   / |
977   //  7 +  +  + 6
978   //    | /9  |
979   //    |/    |
980   //  4 +--+--+ 3
981   //       8
982
983   const SMDS_MeshNode* N1 [6];
984   const SMDS_MeshNode* N2 [6];
985   if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
986     return false;
987   // now we receive following N1 and N2 (using numeration as above image)
988   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
989   // i.e. first nodes from both arrays determ new diagonal
990
991   const SMDS_MeshNode* aNodes[8];
992   aNodes[0] = N1[0];
993   aNodes[1] = N1[1];
994   aNodes[2] = N2[0];
995   aNodes[3] = N2[1];
996   aNodes[4] = N1[3];
997   aNodes[5] = N2[5];
998   aNodes[6] = N2[3];
999   aNodes[7] = N1[5];
1000
1001   const SMDS_MeshElement* newElem = 0;
1002   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1003                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1004   myLastCreatedElems.Append(newElem);
1005   AddToSameGroups( newElem, tr1, aMesh );
1006   int aShapeId = tr1->getshapeId();
1007   if ( aShapeId )
1008     {
1009       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1010     }
1011   aMesh->RemoveElement( tr1 );
1012   aMesh->RemoveElement( tr2 );
1013
1014   // remove middle node (9)
1015   GetMeshDS()->RemoveNode( N1[4] );
1016
1017   return true;
1018 }
1019
1020 //=======================================================================
1021 //function : Reorient
1022 //purpose  : Reverse theElement orientation
1023 //=======================================================================
1024
1025 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1026 {
1027   MESSAGE("Reorient");
1028   myLastCreatedElems.Clear();
1029   myLastCreatedNodes.Clear();
1030
1031   if (!theElem)
1032     return false;
1033   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1034   if ( !it || !it->more() )
1035     return false;
1036
1037   switch ( theElem->GetType() ) {
1038
1039   case SMDSAbs_Edge:
1040   case SMDSAbs_Face: {
1041     if(!theElem->IsQuadratic()) {
1042       int i = theElem->NbNodes();
1043       vector<const SMDS_MeshNode*> aNodes( i );
1044       while ( it->more() )
1045         aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
1046       return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
1047     }
1048     else {
1049       // quadratic elements
1050       if(theElem->GetType()==SMDSAbs_Edge) {
1051         vector<const SMDS_MeshNode*> aNodes(3);
1052         aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1053         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1054         aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1055         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1056       }
1057       else {
1058         int nbn = theElem->NbNodes();
1059         vector<const SMDS_MeshNode*> aNodes(nbn);
1060         aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1061         int i=1;
1062         for(; i<nbn/2; i++) {
1063           aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1064         }
1065         for(i=0; i<nbn/2; i++) {
1066           aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1067         }
1068         return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1069       }
1070     }
1071   }
1072   case SMDSAbs_Volume: {
1073     if (theElem->IsPoly()) {
1074       // TODO reorient vtk polyhedron
1075       MESSAGE("reorient vtk polyhedron ?");
1076       const SMDS_VtkVolume* aPolyedre =
1077         dynamic_cast<const SMDS_VtkVolume*>( theElem );
1078       if (!aPolyedre) {
1079         MESSAGE("Warning: bad volumic element");
1080         return false;
1081       }
1082
1083       int nbFaces = aPolyedre->NbFaces();
1084       vector<const SMDS_MeshNode *> poly_nodes;
1085       vector<int> quantities (nbFaces);
1086
1087       // reverse each face of the polyedre
1088       for (int iface = 1; iface <= nbFaces; iface++) {
1089         int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1090         quantities[iface - 1] = nbFaceNodes;
1091
1092         for (inode = nbFaceNodes; inode >= 1; inode--) {
1093           const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1094           poly_nodes.push_back(curNode);
1095         }
1096       }
1097
1098       return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1099
1100     }
1101     else {
1102       SMDS_VolumeTool vTool;
1103       if ( !vTool.Set( theElem ))
1104         return false;
1105       vTool.Inverse();
1106       MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1107       return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1108     }
1109   }
1110   default:;
1111   }
1112
1113   return false;
1114 }
1115
1116 //================================================================================
1117 /*!
1118  * \brief Reorient faces.
1119  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1120  * \param theDirection - desired direction of normal of \a theFace
1121  * \param theFace - one of \a theFaces that sould be oriented according to
1122  *        \a theDirection and whose orientation defines orientation of other faces
1123  * \return number of reoriented faces.
1124  */
1125 //================================================================================
1126
1127 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1128                                   const gp_Dir&            theDirection,
1129                                   const SMDS_MeshElement * theFace)
1130 {
1131   int nbReori = 0;
1132   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1133
1134   if ( theFaces.empty() )
1135   {
1136     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1137     while ( fIt->more() )
1138       theFaces.insert( theFaces.end(), fIt->next() );
1139   }
1140
1141   // orient theFace according to theDirection
1142   gp_XYZ normal;
1143   SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1144   if ( normal * theDirection.XYZ() < 0 )
1145     nbReori += Reorient( theFace );
1146
1147   // Orient other faces
1148
1149   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1150   TIDSortedElemSet avoidSet;
1151   set< SMESH_TLink > checkedLinks;
1152   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1153
1154   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1155     theFaces.erase( theFace );
1156   startFaces.insert( theFace );
1157
1158   int nodeInd1, nodeInd2;
1159   const SMDS_MeshElement*           otherFace;
1160   vector< const SMDS_MeshElement* > facesNearLink;
1161   vector< std::pair< int, int > >   nodeIndsOfFace;
1162
1163   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1164   while ( !startFaces.empty() )
1165   {
1166     startFace = startFaces.begin();
1167     theFace = *startFace;
1168     startFaces.erase( startFace );
1169     if ( !visitedFaces.insert( theFace ).second )
1170       continue;
1171
1172     avoidSet.clear();
1173     avoidSet.insert(theFace);
1174
1175     NLink link( theFace->GetNode( 0 ), 0 );
1176
1177     const int nbNodes = theFace->NbCornerNodes();
1178     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1179     {
1180       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1181       linkIt_isNew = checkedLinks.insert( link );
1182       if ( !linkIt_isNew.second )
1183       {
1184         // link has already been checked and won't be encountered more
1185         // if the group (theFaces) is manifold
1186         //checkedLinks.erase( linkIt_isNew.first );
1187       }
1188       else
1189       {
1190         facesNearLink.clear();
1191         nodeIndsOfFace.clear();
1192         while (( otherFace = FindFaceInSet( link.first, link.second,
1193                                             theFaces, avoidSet, &nodeInd1, &nodeInd2 )))
1194           if ( otherFace != theFace)
1195           {
1196             facesNearLink.push_back( otherFace );
1197             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1198             avoidSet.insert( otherFace );
1199           }
1200         if ( facesNearLink.size() > 1 )
1201         {
1202           // NON-MANIFOLD mesh shell !
1203           // select a face most co-directed with theFace,
1204           // other faces won't be visited this time
1205           gp_XYZ NF, NOF;
1206           SMESH_Algo::FaceNormal( theFace, NF, /*normalized=*/false );
1207           double proj, maxProj = -1;
1208           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1209             SMESH_Algo::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1210             if (( proj = Abs( NF * NOF )) > maxProj ) {
1211               maxProj = proj;
1212               otherFace = facesNearLink[i];
1213               nodeInd1  = nodeIndsOfFace[i].first;
1214               nodeInd2  = nodeIndsOfFace[i].second;
1215             }
1216           }
1217           // not to visit rejected faces
1218           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1219             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1220               visitedFaces.insert( facesNearLink[i] );
1221         }
1222         else if ( facesNearLink.size() == 1 )
1223         {
1224           otherFace = facesNearLink[0];
1225           nodeInd1  = nodeIndsOfFace.back().first;
1226           nodeInd2  = nodeIndsOfFace.back().second;
1227         }
1228         if ( otherFace && otherFace != theFace)
1229         {
1230           // link must be reverse in otherFace if orientation ot otherFace
1231           // is same as that of theFace
1232           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1233           {
1234             nbReori += Reorient( otherFace );
1235           }
1236           startFaces.insert( otherFace );
1237         }
1238       }
1239       std::swap( link.first, link.second ); // reverse the link
1240     }
1241   }
1242   return nbReori;
1243 }
1244
1245 //=======================================================================
1246 //function : getBadRate
1247 //purpose  :
1248 //=======================================================================
1249
1250 static double getBadRate (const SMDS_MeshElement*               theElem,
1251                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1252 {
1253   SMESH::Controls::TSequenceOfXYZ P;
1254   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1255     return 1e100;
1256   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1257   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1258 }
1259
1260 //=======================================================================
1261 //function : QuadToTri
1262 //purpose  : Cut quadrangles into triangles.
1263 //           theCrit is used to select a diagonal to cut
1264 //=======================================================================
1265
1266 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1267                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1268 {
1269   myLastCreatedElems.Clear();
1270   myLastCreatedNodes.Clear();
1271
1272   MESSAGE( "::QuadToTri()" );
1273
1274   if ( !theCrit.get() )
1275     return false;
1276
1277   SMESHDS_Mesh * aMesh = GetMeshDS();
1278
1279   Handle(Geom_Surface) surface;
1280   SMESH_MesherHelper   helper( *GetMesh() );
1281
1282   TIDSortedElemSet::iterator itElem;
1283   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1284     const SMDS_MeshElement* elem = *itElem;
1285     if ( !elem || elem->GetType() != SMDSAbs_Face )
1286       continue;
1287     if ( elem->NbCornerNodes() != 4 )
1288       continue;
1289
1290     // retrieve element nodes
1291     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1292
1293     // compare two sets of possible triangles
1294     double aBadRate1, aBadRate2; // to what extent a set is bad
1295     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1296     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1297     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1298
1299     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1300     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1301     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1302
1303     int aShapeId = FindShape( elem );
1304     const SMDS_MeshElement* newElem1 = 0;
1305     const SMDS_MeshElement* newElem2 = 0;
1306
1307     if( !elem->IsQuadratic() ) {
1308
1309       // split liner quadrangle
1310       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1311       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1312       if ( aBadRate1 <= aBadRate2 ) {
1313         // tr1 + tr2 is better
1314         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1315         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1316       }
1317       else {
1318         // tr3 + tr4 is better
1319         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1320         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1321       }
1322     }
1323     else {
1324
1325       // split quadratic quadrangle
1326
1327       // get surface elem is on
1328       if ( aShapeId != helper.GetSubShapeID() ) {
1329         surface.Nullify();
1330         TopoDS_Shape shape;
1331         if ( aShapeId > 0 )
1332           shape = aMesh->IndexToShape( aShapeId );
1333         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1334           TopoDS_Face face = TopoDS::Face( shape );
1335           surface = BRep_Tool::Surface( face );
1336           if ( !surface.IsNull() )
1337             helper.SetSubShape( shape );
1338         }
1339       }
1340       // find middle point for (0,1,2,3)
1341       // and create a node in this point;
1342       const SMDS_MeshNode* newN = 0;
1343       if ( aNodes.size() == 9 )
1344       {
1345         // SMDSEntity_BiQuad_Quadrangle
1346         newN = aNodes.back();
1347       }
1348       else
1349       {
1350         gp_XYZ p( 0,0,0 );
1351         if ( surface.IsNull() )
1352         {
1353           for ( int i = 0; i < 4; i++ )
1354             p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1355           p /= 4;
1356         }
1357         else
1358         {
1359           const SMDS_MeshNode* inFaceNode = 0;
1360           if ( helper.GetNodeUVneedInFaceNode() )
1361             for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1362               if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1363                 inFaceNode = aNodes[ i ];
1364
1365           TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1366           gp_XY uv( 0,0 );
1367           for ( int i = 0; i < 4; i++ )
1368             uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1369           uv /= 4.;
1370           p = surface->Value( uv.X(), uv.Y() ).XYZ();
1371         }
1372         newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1373         myLastCreatedNodes.Append(newN);
1374       }
1375       // create a new element
1376       if ( aBadRate1 <= aBadRate2 ) {
1377         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1378                                   aNodes[6], aNodes[7], newN );
1379         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1380                                   newN,      aNodes[4], aNodes[5] );
1381       }
1382       else {
1383         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1384                                   aNodes[7], aNodes[4], newN );
1385         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1386                                   newN,      aNodes[5], aNodes[6] );
1387       }
1388     } // quadratic case
1389
1390     // care of a new element
1391
1392     myLastCreatedElems.Append(newElem1);
1393     myLastCreatedElems.Append(newElem2);
1394     AddToSameGroups( newElem1, elem, aMesh );
1395     AddToSameGroups( newElem2, elem, aMesh );
1396
1397     // put a new triangle on the same shape
1398     if ( aShapeId )
1399       {
1400         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1401         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1402       }
1403     aMesh->RemoveElement( elem );
1404   }
1405   return true;
1406 }
1407
1408 //=======================================================================
1409 //function : BestSplit
1410 //purpose  : Find better diagonal for cutting.
1411 //=======================================================================
1412
1413 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1414                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1415 {
1416   myLastCreatedElems.Clear();
1417   myLastCreatedNodes.Clear();
1418
1419   if (!theCrit.get())
1420     return -1;
1421
1422   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1423     return -1;
1424
1425   if( theQuad->NbNodes()==4 ||
1426       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1427
1428     // retrieve element nodes
1429     const SMDS_MeshNode* aNodes [4];
1430     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1431     int i = 0;
1432     //while (itN->more())
1433     while (i<4) {
1434       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1435     }
1436     // compare two sets of possible triangles
1437     double aBadRate1, aBadRate2; // to what extent a set is bad
1438     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1439     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1440     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1441
1442     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1443     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1444     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1445     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1448       return 1; // diagonal 1-3
1449
1450     return 2; // diagonal 2-4
1451   }
1452   return -1;
1453 }
1454
1455 namespace
1456 {
1457   // Methods of splitting volumes into tetra
1458
1459   const int theHexTo5_1[5*4+1] =
1460     {
1461       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1462     };
1463   const int theHexTo5_2[5*4+1] =
1464     {
1465       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1466     };
1467   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1468
1469   const int theHexTo6_1[6*4+1] =
1470     {
1471       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
1472     };
1473   const int theHexTo6_2[6*4+1] =
1474     {
1475       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
1476     };
1477   const int theHexTo6_3[6*4+1] =
1478     {
1479       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
1480     };
1481   const int theHexTo6_4[6*4+1] =
1482     {
1483       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
1484     };
1485   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1486
1487   const int thePyraTo2_1[2*4+1] =
1488     {
1489       0, 1, 2, 4,    0, 2, 3, 4,   -1
1490     };
1491   const int thePyraTo2_2[2*4+1] =
1492     {
1493       1, 2, 3, 4,    1, 3, 0, 4,   -1
1494     };
1495   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1496
1497   const int thePentaTo3_1[3*4+1] =
1498     {
1499       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1500     };
1501   const int thePentaTo3_2[3*4+1] =
1502     {
1503       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1504     };
1505   const int thePentaTo3_3[3*4+1] =
1506     {
1507       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1508     };
1509   const int thePentaTo3_4[3*4+1] =
1510     {
1511       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1512     };
1513   const int thePentaTo3_5[3*4+1] =
1514     {
1515       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1516     };
1517   const int thePentaTo3_6[3*4+1] =
1518     {
1519       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1520     };
1521   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1522                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1523
1524   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1525   {
1526     int _n1, _n2, _n3;
1527     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1528     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1529     bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1530   };
1531   struct TSplitMethod
1532   {
1533     int        _nbTetra;
1534     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1535     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1536     bool       _ownConn;      //!< to delete _connectivity in destructor
1537     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1538
1539     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1540       : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1541     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1542     bool hasFacet( const TTriangleFacet& facet ) const
1543     {
1544       const int* tetConn = _connectivity;
1545       for ( ; tetConn[0] >= 0; tetConn += 4 )
1546         if (( facet.contains( tetConn[0] ) +
1547               facet.contains( tetConn[1] ) +
1548               facet.contains( tetConn[2] ) +
1549               facet.contains( tetConn[3] )) == 3 )
1550           return true;
1551       return false;
1552     }
1553   };
1554
1555   //=======================================================================
1556   /*!
1557    * \brief return TSplitMethod for the given element
1558    */
1559   //=======================================================================
1560
1561   TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1562   {
1563     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1564
1565     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1566     // an edge and a face barycenter; tertaherdons are based on triangles and
1567     // a volume barycenter
1568     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1569
1570     // Find out how adjacent volumes are split
1571
1572     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1573     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1574     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1575     {
1576       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1577       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1578       if ( nbNodes < 4 ) continue;
1579
1580       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1581       const int* nInd = vol.GetFaceNodesIndices( iF );
1582       if ( nbNodes == 4 )
1583       {
1584         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1585         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1586         if      ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1587         else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1588       }
1589       else
1590       {
1591         int iCom = 0; // common node of triangle faces to split into
1592         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1593         {
1594           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1595                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1596                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1597           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1598                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1599                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1600           if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1601           {
1602             triaSplits.push_back( t012 );
1603             triaSplits.push_back( t023 );
1604             break;
1605           }
1606         }
1607       }
1608       if ( !triaSplits.empty() )
1609         hasAdjacentSplits = true;
1610     }
1611
1612     // Among variants of split method select one compliant with adjacent volumes
1613
1614     TSplitMethod method;
1615     if ( !vol.Element()->IsPoly() && !is24TetMode )
1616     {
1617       int nbVariants = 2, nbTet = 0;
1618       const int** connVariants = 0;
1619       switch ( vol.Element()->GetEntityType() )
1620       {
1621       case SMDSEntity_Hexa:
1622       case SMDSEntity_Quad_Hexa:
1623       case SMDSEntity_TriQuad_Hexa:
1624         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1625           connVariants = theHexTo5, nbTet = 5;
1626         else
1627           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1628         break;
1629       case SMDSEntity_Pyramid:
1630       case SMDSEntity_Quad_Pyramid:
1631         connVariants = thePyraTo2;  nbTet = 2;
1632         break;
1633       case SMDSEntity_Penta:
1634       case SMDSEntity_Quad_Penta:
1635         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1636         break;
1637       default:
1638         nbVariants = 0;
1639       }
1640       for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1641       {
1642         // check method compliancy with adjacent tetras,
1643         // all found splits must be among facets of tetras described by this method
1644         method = TSplitMethod( nbTet, connVariants[variant] );
1645         if ( hasAdjacentSplits && method._nbTetra > 0 )
1646         {
1647           bool facetCreated = true;
1648           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1649           {
1650             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1651             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1652               facetCreated = method.hasFacet( *facet );
1653           }
1654           if ( !facetCreated )
1655             method = TSplitMethod(0); // incompatible method
1656         }
1657       }
1658     }
1659     if ( method._nbTetra < 1 )
1660     {
1661       // No standard method is applicable, use a generic solution:
1662       // each facet of a volume is split into triangles and
1663       // each of triangles and a volume barycenter form a tetrahedron.
1664
1665       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1666
1667       int* connectivity = new int[ maxTetConnSize + 1 ];
1668       method._connectivity = connectivity;
1669       method._ownConn = true;
1670       method._baryNode = !isHex27; // to create central node or not
1671
1672       int connSize = 0;
1673       int baryCenInd = vol.NbNodes() - int( isHex27 );
1674       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1675       {
1676         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1677         const int*   nInd = vol.GetFaceNodesIndices( iF );
1678         // find common node of triangle facets of tetra to create
1679         int iCommon = 0; // index in linear numeration
1680         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1681         if ( !triaSplits.empty() )
1682         {
1683           // by found facets
1684           const TTriangleFacet* facet = &triaSplits.front();
1685           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1686             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1687                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1688               break;
1689         }
1690         else if ( nbNodes > 3 && !is24TetMode )
1691         {
1692           // find the best method of splitting into triangles by aspect ratio
1693           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1694           map< double, int > badness2iCommon;
1695           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1696           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1697           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1698           {
1699             double badness = 0;
1700             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1701             {
1702               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1703                                       nodes[ iQ*((iLast-1)%nbNodes)],
1704                                       nodes[ iQ*((iLast  )%nbNodes)]);
1705               badness += getBadRate( &tria, aspectRatio );
1706             }
1707             badness2iCommon.insert( make_pair( badness, iCommon ));
1708           }
1709           // use iCommon with lowest badness
1710           iCommon = badness2iCommon.begin()->second;
1711         }
1712         if ( iCommon >= nbNodes )
1713           iCommon = 0; // something wrong
1714
1715         // fill connectivity of tetrahedra based on a current face
1716         int nbTet = nbNodes - 2;
1717         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1718         {
1719           int faceBaryCenInd;
1720           if ( isHex27 )
1721           {
1722             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1723             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1724           }
1725           else
1726           {
1727             method._faceBaryNode[ iF ] = 0;
1728             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1729           }
1730           nbTet = nbNodes;
1731           for ( int i = 0; i < nbTet; ++i )
1732           {
1733             int i1 = i, i2 = (i+1) % nbNodes;
1734             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1735             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1736             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1737             connectivity[ connSize++ ] = faceBaryCenInd;
1738             connectivity[ connSize++ ] = baryCenInd;
1739           }
1740         }
1741         else
1742         {
1743           for ( int i = 0; i < nbTet; ++i )
1744           {
1745             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1746             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1747             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1748             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1749             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1750             connectivity[ connSize++ ] = baryCenInd;
1751           }
1752         }
1753         method._nbTetra += nbTet;
1754
1755       } // loop on volume faces
1756
1757       connectivity[ connSize++ ] = -1;
1758
1759     } // end of generic solution
1760
1761     return method;
1762   }
1763   //================================================================================
1764   /*!
1765    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1766    */
1767   //================================================================================
1768
1769   bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1770   {
1771     // find the tetrahedron including the three nodes of facet
1772     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1773     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1774     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1775     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1776     while ( volIt1->more() )
1777     {
1778       const SMDS_MeshElement* v = volIt1->next();
1779       SMDSAbs_EntityType type = v->GetEntityType();
1780       if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1781         continue;
1782       if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1783         continue; // medium node not allowed
1784       const int ind2 = v->GetNodeIndex( n2 );
1785       if ( ind2 < 0 || 3 < ind2 )
1786         continue;
1787       const int ind3 = v->GetNodeIndex( n3 );
1788       if ( ind3 < 0 || 3 < ind3 )
1789         continue;
1790       return true;
1791     }
1792     return false;
1793   }
1794
1795   //=======================================================================
1796   /*!
1797    * \brief A key of a face of volume
1798    */
1799   //=======================================================================
1800
1801   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1802   {
1803     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1804     {
1805       TIDSortedNodeSet sortedNodes;
1806       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1807       int nbNodes = vol.NbFaceNodes( iF );
1808       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1809       for ( int i = 0; i < nbNodes; i += iQ )
1810         sortedNodes.insert( fNodes[i] );
1811       TIDSortedNodeSet::iterator n = sortedNodes.begin();
1812       first.first   = (*(n++))->GetID();
1813       first.second  = (*(n++))->GetID();
1814       second.first  = (*(n++))->GetID();
1815       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1816     }
1817   };
1818 } // namespace
1819
1820 //=======================================================================
1821 //function : SplitVolumesIntoTetra
1822 //purpose  : Split volume elements into tetrahedra.
1823 //=======================================================================
1824
1825 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1826                                               const int                theMethodFlags)
1827 {
1828   // std-like iterator on coordinates of nodes of mesh element
1829   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1830   NXyzIterator xyzEnd;
1831
1832   SMDS_VolumeTool    volTool;
1833   SMESH_MesherHelper helper( *GetMesh());
1834
1835   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1836   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1837
1838   SMESH_SequenceOfElemPtr newNodes, newElems;
1839
1840   // map face of volume to it's baricenrtic node
1841   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1842   double bc[3];
1843
1844   TIDSortedElemSet::const_iterator elem = theElems.begin();
1845   for ( ; elem != theElems.end(); ++elem )
1846   {
1847     if ( (*elem)->GetType() != SMDSAbs_Volume )
1848       continue;
1849     SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1850     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1851       continue;
1852
1853     if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1854
1855     TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1856     if ( splitMethod._nbTetra < 1 ) continue;
1857
1858     // find submesh to add new tetras to
1859     if ( !subMesh || !subMesh->Contains( *elem ))
1860     {
1861       int shapeID = FindShape( *elem );
1862       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1863       subMesh = GetMeshDS()->MeshElements( shapeID );
1864     }
1865     int iQ;
1866     if ( (*elem)->IsQuadratic() )
1867     {
1868       iQ = 2;
1869       // add quadratic links to the helper
1870       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1871       {
1872         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1873         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1874         for ( int iN = 0; iN < nbN; iN += iQ )
1875           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1876       }
1877       helper.SetIsQuadratic( true );
1878     }
1879     else
1880     {
1881       iQ = 1;
1882       helper.SetIsQuadratic( false );
1883     }
1884     vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1885     helper.SetElementsOnShape( true );
1886     if ( splitMethod._baryNode )
1887     {
1888       // make a node at barycenter
1889       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1890       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1891       nodes.push_back( gcNode );
1892       newNodes.Append( gcNode );
1893     }
1894     if ( !splitMethod._faceBaryNode.empty() )
1895     {
1896       // make or find baricentric nodes of faces
1897       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1898       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1899       {
1900         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1901           volFace2BaryNode.insert
1902           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1903         if ( !f_n->second )
1904         {
1905           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1906           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1907         }
1908         nodes.push_back( iF_n->second = f_n->second );
1909       }
1910     }
1911
1912     // make tetras
1913     vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1914     const int* tetConn = splitMethod._connectivity;
1915     for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1916       newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1917                                                        nodes[ tetConn[1] ],
1918                                                        nodes[ tetConn[2] ],
1919                                                        nodes[ tetConn[3] ]));
1920
1921     ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1922
1923     // Split faces on sides of the split volume
1924
1925     const SMDS_MeshNode** volNodes = volTool.GetNodes();
1926     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1927     {
1928       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1929       if ( nbNodes < 4 ) continue;
1930
1931       // find an existing face
1932       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1933                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1934       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1935                                                                        /*noMedium=*/false))
1936       {
1937         // make triangles
1938         helper.SetElementsOnShape( false );
1939         vector< const SMDS_MeshElement* > triangles;
1940
1941         // find submesh to add new triangles in
1942         if ( !fSubMesh || !fSubMesh->Contains( face ))
1943         {
1944           int shapeID = FindShape( face );
1945           fSubMesh = GetMeshDS()->MeshElements( shapeID );
1946         }
1947         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1948         if ( iF_n != splitMethod._faceBaryNode.end() )
1949         {
1950           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1951           {
1952             const SMDS_MeshNode* n1 = fNodes[iN];
1953             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1954             const SMDS_MeshNode *n3 = iF_n->second;
1955             if ( !volTool.IsFaceExternal( iF ))
1956               swap( n2, n3 );
1957             triangles.push_back( helper.AddFace( n1,n2,n3 ));
1958
1959             if ( fSubMesh && n3->getshapeId() < 1 )
1960               fSubMesh->AddNode( n3 );
1961           }
1962         }
1963         else
1964         {
1965           // among possible triangles create ones discribed by split method
1966           const int* nInd = volTool.GetFaceNodesIndices( iF );
1967           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1968           int iCom = 0; // common node of triangle faces to split into
1969           list< TTriangleFacet > facets;
1970           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1971           {
1972             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
1973                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
1974                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
1975             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
1976                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
1977                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
1978             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1979             {
1980               facets.push_back( t012 );
1981               facets.push_back( t023 );
1982               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1983                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
1984                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
1985                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
1986               break;
1987             }
1988           }
1989           list< TTriangleFacet >::iterator facet = facets.begin();
1990           for ( ; facet != facets.end(); ++facet )
1991           {
1992             if ( !volTool.IsFaceExternal( iF ))
1993               swap( facet->_n2, facet->_n3 );
1994             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1995                                                  volNodes[ facet->_n2 ],
1996                                                  volNodes[ facet->_n3 ]));
1997           }
1998         }
1999         for ( int i = 0; i < triangles.size(); ++i )
2000         {
2001           if ( !triangles[i] ) continue;
2002           if ( fSubMesh )
2003             fSubMesh->AddElement( triangles[i]);
2004           newElems.Append( triangles[i] );
2005         }
2006         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2007         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2008       }
2009
2010     } // loop on volume faces to split them into triangles
2011
2012     GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2013
2014     if ( geomType == SMDSEntity_TriQuad_Hexa )
2015     {
2016       // remove medium nodes that could become free
2017       for ( int i = 20; i < volTool.NbNodes(); ++i )
2018         if ( volNodes[i]->NbInverseElements() == 0 )
2019           GetMeshDS()->RemoveNode( volNodes[i] );
2020     }
2021   } // loop on volumes to split
2022
2023   myLastCreatedNodes = newNodes;
2024   myLastCreatedElems = newElems;
2025 }
2026
2027 //=======================================================================
2028 //function : AddToSameGroups
2029 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2030 //=======================================================================
2031
2032 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2033                                         const SMDS_MeshElement* elemInGroups,
2034                                         SMESHDS_Mesh *          aMesh)
2035 {
2036   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2037   if (!groups.empty()) {
2038     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2039     for ( ; grIt != groups.end(); grIt++ ) {
2040       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2041       if ( group && group->Contains( elemInGroups ))
2042         group->SMDSGroup().Add( elemToAdd );
2043     }
2044   }
2045 }
2046
2047
2048 //=======================================================================
2049 //function : RemoveElemFromGroups
2050 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2051 //=======================================================================
2052 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2053                                              SMESHDS_Mesh *          aMesh)
2054 {
2055   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2056   if (!groups.empty())
2057   {
2058     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2059     for (; GrIt != groups.end(); GrIt++)
2060     {
2061       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2062       if (!grp || grp->IsEmpty()) continue;
2063       grp->SMDSGroup().Remove(removeelem);
2064     }
2065   }
2066 }
2067
2068 //================================================================================
2069 /*!
2070  * \brief Replace elemToRm by elemToAdd in the all groups
2071  */
2072 //================================================================================
2073
2074 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2075                                             const SMDS_MeshElement* elemToAdd,
2076                                             SMESHDS_Mesh *          aMesh)
2077 {
2078   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2079   if (!groups.empty()) {
2080     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2081     for ( ; grIt != groups.end(); grIt++ ) {
2082       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2083       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2084         group->SMDSGroup().Add( elemToAdd );
2085     }
2086   }
2087 }
2088
2089 //================================================================================
2090 /*!
2091  * \brief Replace elemToRm by elemToAdd in the all groups
2092  */
2093 //================================================================================
2094
2095 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2096                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2097                                             SMESHDS_Mesh *                         aMesh)
2098 {
2099   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2100   if (!groups.empty())
2101   {
2102     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2103     for ( ; grIt != groups.end(); grIt++ ) {
2104       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2105       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2106         for ( int i = 0; i < elemToAdd.size(); ++i )
2107           group->SMDSGroup().Add( elemToAdd[ i ] );
2108     }
2109   }
2110 }
2111
2112 //=======================================================================
2113 //function : QuadToTri
2114 //purpose  : Cut quadrangles into triangles.
2115 //           theCrit is used to select a diagonal to cut
2116 //=======================================================================
2117
2118 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2119                                   const bool         the13Diag)
2120 {
2121   myLastCreatedElems.Clear();
2122   myLastCreatedNodes.Clear();
2123
2124   MESSAGE( "::QuadToTri()" );
2125
2126   SMESHDS_Mesh * aMesh = GetMeshDS();
2127
2128   Handle(Geom_Surface) surface;
2129   SMESH_MesherHelper   helper( *GetMesh() );
2130
2131   TIDSortedElemSet::iterator itElem;
2132   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2133     const SMDS_MeshElement* elem = *itElem;
2134     if ( !elem || elem->GetType() != SMDSAbs_Face )
2135       continue;
2136     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2137     if(!isquad) continue;
2138
2139     if(elem->NbNodes()==4) {
2140       // retrieve element nodes
2141       const SMDS_MeshNode* aNodes [4];
2142       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2143       int i = 0;
2144       while ( itN->more() )
2145         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2146
2147       int aShapeId = FindShape( elem );
2148       const SMDS_MeshElement* newElem1 = 0;
2149       const SMDS_MeshElement* newElem2 = 0;
2150       if ( the13Diag ) {
2151         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2152         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2153       }
2154       else {
2155         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2156         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2157       }
2158       myLastCreatedElems.Append(newElem1);
2159       myLastCreatedElems.Append(newElem2);
2160       // put a new triangle on the same shape and add to the same groups
2161       if ( aShapeId )
2162         {
2163           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2164           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2165         }
2166       AddToSameGroups( newElem1, elem, aMesh );
2167       AddToSameGroups( newElem2, elem, aMesh );
2168       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2169       aMesh->RemoveElement( elem );
2170     }
2171
2172     // Quadratic quadrangle
2173
2174     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2175
2176       // get surface elem is on
2177       int aShapeId = FindShape( elem );
2178       if ( aShapeId != helper.GetSubShapeID() ) {
2179         surface.Nullify();
2180         TopoDS_Shape shape;
2181         if ( aShapeId > 0 )
2182           shape = aMesh->IndexToShape( aShapeId );
2183         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2184           TopoDS_Face face = TopoDS::Face( shape );
2185           surface = BRep_Tool::Surface( face );
2186           if ( !surface.IsNull() )
2187             helper.SetSubShape( shape );
2188         }
2189       }
2190
2191       const SMDS_MeshNode* aNodes [8];
2192       const SMDS_MeshNode* inFaceNode = 0;
2193       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2194       int i = 0;
2195       while ( itN->more() ) {
2196         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2197         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2198              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2199         {
2200           inFaceNode = aNodes[ i-1 ];
2201         }
2202       }
2203
2204       // find middle point for (0,1,2,3)
2205       // and create a node in this point;
2206       gp_XYZ p( 0,0,0 );
2207       if ( surface.IsNull() ) {
2208         for(i=0; i<4; i++)
2209           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2210         p /= 4;
2211       }
2212       else {
2213         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2214         gp_XY uv( 0,0 );
2215         for(i=0; i<4; i++)
2216           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2217         uv /= 4.;
2218         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2219       }
2220       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2221       myLastCreatedNodes.Append(newN);
2222
2223       // create a new element
2224       const SMDS_MeshElement* newElem1 = 0;
2225       const SMDS_MeshElement* newElem2 = 0;
2226       if ( the13Diag ) {
2227         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2228                                   aNodes[6], aNodes[7], newN );
2229         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2230                                   newN,      aNodes[4], aNodes[5] );
2231       }
2232       else {
2233         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2234                                   aNodes[7], aNodes[4], newN );
2235         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2236                                   newN,      aNodes[5], aNodes[6] );
2237       }
2238       myLastCreatedElems.Append(newElem1);
2239       myLastCreatedElems.Append(newElem2);
2240       // put a new triangle on the same shape and add to the same groups
2241       if ( aShapeId )
2242         {
2243           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2244           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2245         }
2246       AddToSameGroups( newElem1, elem, aMesh );
2247       AddToSameGroups( newElem2, elem, aMesh );
2248       aMesh->RemoveElement( elem );
2249     }
2250   }
2251
2252   return true;
2253 }
2254
2255 //=======================================================================
2256 //function : getAngle
2257 //purpose  :
2258 //=======================================================================
2259
2260 double getAngle(const SMDS_MeshElement * tr1,
2261                 const SMDS_MeshElement * tr2,
2262                 const SMDS_MeshNode *    n1,
2263                 const SMDS_MeshNode *    n2)
2264 {
2265   double angle = 2. * M_PI; // bad angle
2266
2267   // get normals
2268   SMESH::Controls::TSequenceOfXYZ P1, P2;
2269   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2270        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2271     return angle;
2272   gp_Vec N1,N2;
2273   if(!tr1->IsQuadratic())
2274     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2275   else
2276     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2277   if ( N1.SquareMagnitude() <= gp::Resolution() )
2278     return angle;
2279   if(!tr2->IsQuadratic())
2280     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2281   else
2282     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2283   if ( N2.SquareMagnitude() <= gp::Resolution() )
2284     return angle;
2285
2286   // find the first diagonal node n1 in the triangles:
2287   // take in account a diagonal link orientation
2288   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2289   for ( int t = 0; t < 2; t++ ) {
2290     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2291     int i = 0, iDiag = -1;
2292     while ( it->more()) {
2293       const SMDS_MeshElement *n = it->next();
2294       if ( n == n1 || n == n2 ) {
2295         if ( iDiag < 0)
2296           iDiag = i;
2297         else {
2298           if ( i - iDiag == 1 )
2299             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2300           else
2301             nFirst[ t ] = n;
2302           break;
2303         }
2304       }
2305       i++;
2306     }
2307   }
2308   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2309     N2.Reverse();
2310
2311   angle = N1.Angle( N2 );
2312   //SCRUTE( angle );
2313   return angle;
2314 }
2315
2316 // =================================================
2317 // class generating a unique ID for a pair of nodes
2318 // and able to return nodes by that ID
2319 // =================================================
2320 class LinkID_Gen {
2321 public:
2322
2323   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2324     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2325   {}
2326
2327   long GetLinkID (const SMDS_MeshNode * n1,
2328                   const SMDS_MeshNode * n2) const
2329   {
2330     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2331   }
2332
2333   bool GetNodes (const long             theLinkID,
2334                  const SMDS_MeshNode* & theNode1,
2335                  const SMDS_MeshNode* & theNode2) const
2336   {
2337     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2338     if ( !theNode1 ) return false;
2339     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2340     if ( !theNode2 ) return false;
2341     return true;
2342   }
2343
2344 private:
2345   LinkID_Gen();
2346   const SMESHDS_Mesh* myMesh;
2347   long                myMaxID;
2348 };
2349
2350
2351 //=======================================================================
2352 //function : TriToQuad
2353 //purpose  : Fuse neighbour triangles into quadrangles.
2354 //           theCrit is used to select a neighbour to fuse with.
2355 //           theMaxAngle is a max angle between element normals at which
2356 //           fusion is still performed.
2357 //=======================================================================
2358
2359 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2360                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2361                                   const double                         theMaxAngle)
2362 {
2363   myLastCreatedElems.Clear();
2364   myLastCreatedNodes.Clear();
2365
2366   MESSAGE( "::TriToQuad()" );
2367
2368   if ( !theCrit.get() )
2369     return false;
2370
2371   SMESHDS_Mesh * aMesh = GetMeshDS();
2372
2373   // Prepare data for algo: build
2374   // 1. map of elements with their linkIDs
2375   // 2. map of linkIDs with their elements
2376
2377   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2378   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2379   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2380   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2381
2382   TIDSortedElemSet::iterator itElem;
2383   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2384     const SMDS_MeshElement* elem = *itElem;
2385     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2386     bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2387     if(!IsTria) continue;
2388
2389     // retrieve element nodes
2390     const SMDS_MeshNode* aNodes [4];
2391     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2392     int i = 0;
2393     while ( i<3 )
2394       aNodes[ i++ ] = cast2Node( itN->next() );
2395     aNodes[ 3 ] = aNodes[ 0 ];
2396
2397     // fill maps
2398     for ( i = 0; i < 3; i++ ) {
2399       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2400       // check if elements sharing a link can be fused
2401       itLE = mapLi_listEl.find( link );
2402       if ( itLE != mapLi_listEl.end() ) {
2403         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2404           continue;
2405         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2406         //if ( FindShape( elem ) != FindShape( elem2 ))
2407         //  continue; // do not fuse triangles laying on different shapes
2408         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2409           continue; // avoid making badly shaped quads
2410         (*itLE).second.push_back( elem );
2411       }
2412       else {
2413         mapLi_listEl[ link ].push_back( elem );
2414       }
2415       mapEl_setLi [ elem ].insert( link );
2416     }
2417   }
2418   // Clean the maps from the links shared by a sole element, ie
2419   // links to which only one element is bound in mapLi_listEl
2420
2421   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2422     int nbElems = (*itLE).second.size();
2423     if ( nbElems < 2  ) {
2424       const SMDS_MeshElement* elem = (*itLE).second.front();
2425       SMESH_TLink link = (*itLE).first;
2426       mapEl_setLi[ elem ].erase( link );
2427       if ( mapEl_setLi[ elem ].empty() )
2428         mapEl_setLi.erase( elem );
2429     }
2430   }
2431
2432   // Algo: fuse triangles into quadrangles
2433
2434   while ( ! mapEl_setLi.empty() ) {
2435     // Look for the start element:
2436     // the element having the least nb of shared links
2437     const SMDS_MeshElement* startElem = 0;
2438     int minNbLinks = 4;
2439     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2440       int nbLinks = (*itEL).second.size();
2441       if ( nbLinks < minNbLinks ) {
2442         startElem = (*itEL).first;
2443         minNbLinks = nbLinks;
2444         if ( minNbLinks == 1 )
2445           break;
2446       }
2447     }
2448
2449     // search elements to fuse starting from startElem or links of elements
2450     // fused earlyer - startLinks
2451     list< SMESH_TLink > startLinks;
2452     while ( startElem || !startLinks.empty() ) {
2453       while ( !startElem && !startLinks.empty() ) {
2454         // Get an element to start, by a link
2455         SMESH_TLink linkId = startLinks.front();
2456         startLinks.pop_front();
2457         itLE = mapLi_listEl.find( linkId );
2458         if ( itLE != mapLi_listEl.end() ) {
2459           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2460           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2461           for ( ; itE != listElem.end() ; itE++ )
2462             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2463               startElem = (*itE);
2464           mapLi_listEl.erase( itLE );
2465         }
2466       }
2467
2468       if ( startElem ) {
2469         // Get candidates to be fused
2470         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2471         const SMESH_TLink *link12, *link13;
2472         startElem = 0;
2473         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2474         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2475         ASSERT( !setLi.empty() );
2476         set< SMESH_TLink >::iterator itLi;
2477         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2478         {
2479           const SMESH_TLink & link = (*itLi);
2480           itLE = mapLi_listEl.find( link );
2481           if ( itLE == mapLi_listEl.end() )
2482             continue;
2483
2484           const SMDS_MeshElement* elem = (*itLE).second.front();
2485           if ( elem == tr1 )
2486             elem = (*itLE).second.back();
2487           mapLi_listEl.erase( itLE );
2488           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2489             continue;
2490           if ( tr2 ) {
2491             tr3 = elem;
2492             link13 = &link;
2493           }
2494           else {
2495             tr2 = elem;
2496             link12 = &link;
2497           }
2498
2499           // add other links of elem to list of links to re-start from
2500           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2501           set< SMESH_TLink >::iterator it;
2502           for ( it = links.begin(); it != links.end(); it++ ) {
2503             const SMESH_TLink& link2 = (*it);
2504             if ( link2 != link )
2505               startLinks.push_back( link2 );
2506           }
2507         }
2508
2509         // Get nodes of possible quadrangles
2510         const SMDS_MeshNode *n12 [4], *n13 [4];
2511         bool Ok12 = false, Ok13 = false;
2512         const SMDS_MeshNode *linkNode1, *linkNode2;
2513         if(tr2) {
2514           linkNode1 = link12->first;
2515           linkNode2 = link12->second;
2516           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2517             Ok12 = true;
2518         }
2519         if(tr3) {
2520           linkNode1 = link13->first;
2521           linkNode2 = link13->second;
2522           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2523             Ok13 = true;
2524         }
2525
2526         // Choose a pair to fuse
2527         if ( Ok12 && Ok13 ) {
2528           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2529           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2530           double aBadRate12 = getBadRate( &quad12, theCrit );
2531           double aBadRate13 = getBadRate( &quad13, theCrit );
2532           if (  aBadRate13 < aBadRate12 )
2533             Ok12 = false;
2534           else
2535             Ok13 = false;
2536         }
2537
2538         // Make quadrangles
2539         // and remove fused elems and removed links from the maps
2540         mapEl_setLi.erase( tr1 );
2541         if ( Ok12 ) {
2542           mapEl_setLi.erase( tr2 );
2543           mapLi_listEl.erase( *link12 );
2544           if(tr1->NbNodes()==3) {
2545             const SMDS_MeshElement* newElem = 0;
2546             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2547             myLastCreatedElems.Append(newElem);
2548             AddToSameGroups( newElem, tr1, aMesh );
2549             int aShapeId = tr1->getshapeId();
2550             if ( aShapeId )
2551               {
2552                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2553               }
2554             aMesh->RemoveElement( tr1 );
2555             aMesh->RemoveElement( tr2 );
2556           }
2557           else {
2558             const SMDS_MeshNode* N1 [6];
2559             const SMDS_MeshNode* N2 [6];
2560             GetNodesFromTwoTria(tr1,tr2,N1,N2);
2561             // now we receive following N1 and N2 (using numeration as above image)
2562             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2563             // i.e. first nodes from both arrays determ new diagonal
2564             const SMDS_MeshNode* aNodes[8];
2565             aNodes[0] = N1[0];
2566             aNodes[1] = N1[1];
2567             aNodes[2] = N2[0];
2568             aNodes[3] = N2[1];
2569             aNodes[4] = N1[3];
2570             aNodes[5] = N2[5];
2571             aNodes[6] = N2[3];
2572             aNodes[7] = N1[5];
2573             const SMDS_MeshElement* newElem = 0;
2574             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2575                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2576             myLastCreatedElems.Append(newElem);
2577             AddToSameGroups( newElem, tr1, aMesh );
2578             int aShapeId = tr1->getshapeId();
2579             if ( aShapeId )
2580               {
2581                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2582               }
2583             aMesh->RemoveElement( tr1 );
2584             aMesh->RemoveElement( tr2 );
2585             // remove middle node (9)
2586             GetMeshDS()->RemoveNode( N1[4] );
2587           }
2588         }
2589         else if ( Ok13 ) {
2590           mapEl_setLi.erase( tr3 );
2591           mapLi_listEl.erase( *link13 );
2592           if(tr1->NbNodes()==3) {
2593             const SMDS_MeshElement* newElem = 0;
2594             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2595             myLastCreatedElems.Append(newElem);
2596             AddToSameGroups( newElem, tr1, aMesh );
2597             int aShapeId = tr1->getshapeId();
2598             if ( aShapeId )
2599               {
2600                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2601               }
2602             aMesh->RemoveElement( tr1 );
2603             aMesh->RemoveElement( tr3 );
2604           }
2605           else {
2606             const SMDS_MeshNode* N1 [6];
2607             const SMDS_MeshNode* N2 [6];
2608             GetNodesFromTwoTria(tr1,tr3,N1,N2);
2609             // now we receive following N1 and N2 (using numeration as above image)
2610             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
2611             // i.e. first nodes from both arrays determ new diagonal
2612             const SMDS_MeshNode* aNodes[8];
2613             aNodes[0] = N1[0];
2614             aNodes[1] = N1[1];
2615             aNodes[2] = N2[0];
2616             aNodes[3] = N2[1];
2617             aNodes[4] = N1[3];
2618             aNodes[5] = N2[5];
2619             aNodes[6] = N2[3];
2620             aNodes[7] = N1[5];
2621             const SMDS_MeshElement* newElem = 0;
2622             newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2623                                      aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2624             myLastCreatedElems.Append(newElem);
2625             AddToSameGroups( newElem, tr1, aMesh );
2626             int aShapeId = tr1->getshapeId();
2627             if ( aShapeId )
2628               {
2629                 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2630               }
2631             aMesh->RemoveElement( tr1 );
2632             aMesh->RemoveElement( tr3 );
2633             // remove middle node (9)
2634             GetMeshDS()->RemoveNode( N1[4] );
2635           }
2636         }
2637
2638         // Next element to fuse: the rejected one
2639         if ( tr3 )
2640           startElem = Ok12 ? tr3 : tr2;
2641
2642       } // if ( startElem )
2643     } // while ( startElem || !startLinks.empty() )
2644   } // while ( ! mapEl_setLi.empty() )
2645
2646   return true;
2647 }
2648
2649
2650 /*#define DUMPSO(txt) \
2651 //  cout << txt << endl;
2652 //=============================================================================
2653 //
2654 //
2655 //
2656 //=============================================================================
2657 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2658 {
2659 if ( i1 == i2 )
2660 return;
2661 int tmp = idNodes[ i1 ];
2662 idNodes[ i1 ] = idNodes[ i2 ];
2663 idNodes[ i2 ] = tmp;
2664 gp_Pnt Ptmp = P[ i1 ];
2665 P[ i1 ] = P[ i2 ];
2666 P[ i2 ] = Ptmp;
2667 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2668 }
2669
2670 //=======================================================================
2671 //function : SortQuadNodes
2672 //purpose  : Set 4 nodes of a quadrangle face in a good order.
2673 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
2674 //           1 or 2 else 0.
2675 //=======================================================================
2676
2677 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2678 int               idNodes[] )
2679 {
2680   gp_Pnt P[4];
2681   int i;
2682   for ( i = 0; i < 4; i++ ) {
2683     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2684     if ( !n ) return 0;
2685     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2686   }
2687
2688   gp_Vec V1(P[0], P[1]);
2689   gp_Vec V2(P[0], P[2]);
2690   gp_Vec V3(P[0], P[3]);
2691
2692   gp_Vec Cross1 = V1 ^ V2;
2693   gp_Vec Cross2 = V2 ^ V3;
2694
2695   i = 0;
2696   if (Cross1.Dot(Cross2) < 0)
2697   {
2698     Cross1 = V2 ^ V1;
2699     Cross2 = V1 ^ V3;
2700
2701     if (Cross1.Dot(Cross2) < 0)
2702       i = 2;
2703     else
2704       i = 1;
2705     swap ( i, i + 1, idNodes, P );
2706
2707     //     for ( int ii = 0; ii < 4; ii++ ) {
2708     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2709     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2710     //     }
2711   }
2712   return i;
2713 }
2714
2715 //=======================================================================
2716 //function : SortHexaNodes
2717 //purpose  : Set 8 nodes of a hexahedron in a good order.
2718 //           Return success status
2719 //=======================================================================
2720
2721 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2722                                       int               idNodes[] )
2723 {
2724   gp_Pnt P[8];
2725   int i;
2726   DUMPSO( "INPUT: ========================================");
2727   for ( i = 0; i < 8; i++ ) {
2728     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2729     if ( !n ) return false;
2730     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2731     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2732   }
2733   DUMPSO( "========================================");
2734
2735
2736   set<int> faceNodes;  // ids of bottom face nodes, to be found
2737   set<int> checkedId1; // ids of tried 2-nd nodes
2738   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2739   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
2740   int iMin, iLoop1 = 0;
2741
2742   // Loop to try the 2-nd nodes
2743
2744   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2745   {
2746     // Find not checked 2-nd node
2747     for ( i = 1; i < 8; i++ )
2748       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2749         int id1 = idNodes[i];
2750         swap ( 1, i, idNodes, P );
2751         checkedId1.insert ( id1 );
2752         break;
2753       }
2754
2755     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2756     // ie that all but meybe one (id3 which is on the same face) nodes
2757     // lay on the same side from the triangle plane.
2758
2759     bool manyInPlane = false; // more than 4 nodes lay in plane
2760     int iLoop2 = 0;
2761     while ( ++iLoop2 < 6 ) {
2762
2763       // get 1-2-3 plane coeffs
2764       Standard_Real A, B, C, D;
2765       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2766       if ( N.SquareMagnitude() > gp::Resolution() )
2767       {
2768         gp_Pln pln ( P[0], N );
2769         pln.Coefficients( A, B, C, D );
2770
2771         // find the node (iMin) closest to pln
2772         Standard_Real dist[ 8 ], minDist = DBL_MAX;
2773         set<int> idInPln;
2774         for ( i = 3; i < 8; i++ ) {
2775           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2776           if ( fabs( dist[i] ) < minDist ) {
2777             minDist = fabs( dist[i] );
2778             iMin = i;
2779           }
2780           if ( fabs( dist[i] ) <= tol )
2781             idInPln.insert( idNodes[i] );
2782         }
2783
2784         // there should not be more than 4 nodes in bottom plane
2785         if ( idInPln.size() > 1 )
2786         {
2787           DUMPSO( "### idInPln.size() = " << idInPln.size());
2788           // idInPlane does not contain the first 3 nodes
2789           if ( manyInPlane || idInPln.size() == 5)
2790             return false; // all nodes in one plane
2791           manyInPlane = true;
2792
2793           // set the 1-st node to be not in plane
2794           for ( i = 3; i < 8; i++ ) {
2795             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2796               DUMPSO( "### Reset 0-th node");
2797               swap( 0, i, idNodes, P );
2798               break;
2799             }
2800           }
2801
2802           // reset to re-check second nodes
2803           leastDist = DBL_MAX;
2804           faceNodes.clear();
2805           checkedId1.clear();
2806           iLoop1 = 0;
2807           break; // from iLoop2;
2808         }
2809
2810         // check that the other 4 nodes are on the same side
2811         bool sameSide = true;
2812         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2813         for ( i = 3; sameSide && i < 8; i++ ) {
2814           if ( i != iMin )
2815             sameSide = ( isNeg == dist[i] <= 0.);
2816         }
2817
2818         // keep best solution
2819         if ( sameSide && minDist < leastDist ) {
2820           leastDist = minDist;
2821           faceNodes.clear();
2822           faceNodes.insert( idNodes[ 1 ] );
2823           faceNodes.insert( idNodes[ 2 ] );
2824           faceNodes.insert( idNodes[ iMin ] );
2825           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2826                   << " leastDist = " << leastDist);
2827           if ( leastDist <= DBL_MIN )
2828             break;
2829         }
2830       }
2831
2832       // set next 3-d node to check
2833       int iNext = 2 + iLoop2;
2834       if ( iNext < 8 ) {
2835         DUMPSO( "Try 2-nd");
2836         swap ( 2, iNext, idNodes, P );
2837       }
2838     } // while ( iLoop2 < 6 )
2839   } // iLoop1
2840
2841   if ( faceNodes.empty() ) return false;
2842
2843   // Put the faceNodes in proper places
2844   for ( i = 4; i < 8; i++ ) {
2845     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2846       // find a place to put
2847       int iTo = 1;
2848       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2849         iTo++;
2850       DUMPSO( "Set faceNodes");
2851       swap ( iTo, i, idNodes, P );
2852     }
2853   }
2854
2855
2856   // Set nodes of the found bottom face in good order
2857   DUMPSO( " Found bottom face: ");
2858   i = SortQuadNodes( theMesh, idNodes );
2859   if ( i ) {
2860     gp_Pnt Ptmp = P[ i ];
2861     P[ i ] = P[ i+1 ];
2862     P[ i+1 ] = Ptmp;
2863   }
2864   //   else
2865   //     for ( int ii = 0; ii < 4; ii++ ) {
2866   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2867   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2868   //    }
2869
2870   // Gravity center of the top and bottom faces
2871   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2872   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2873
2874   // Get direction from the bottom to the top face
2875   gp_Vec upDir ( aGCb, aGCt );
2876   Standard_Real upDirSize = upDir.Magnitude();
2877   if ( upDirSize <= gp::Resolution() ) return false;
2878   upDir / upDirSize;
2879
2880   // Assure that the bottom face normal points up
2881   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2882   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2883   if ( Nb.Dot( upDir ) < 0 ) {
2884     DUMPSO( "Reverse bottom face");
2885     swap( 1, 3, idNodes, P );
2886   }
2887
2888   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2889   Standard_Real minDist = DBL_MAX;
2890   for ( i = 4; i < 8; i++ ) {
2891     // projection of P[i] to the plane defined by P[0] and upDir
2892     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2893     Standard_Real sqDist = P[0].SquareDistance( Pp );
2894     if ( sqDist < minDist ) {
2895       minDist = sqDist;
2896       iMin = i;
2897     }
2898   }
2899   DUMPSO( "Set 4-th");
2900   swap ( 4, iMin, idNodes, P );
2901
2902   // Set nodes of the top face in good order
2903   DUMPSO( "Sort top face");
2904   i = SortQuadNodes( theMesh, &idNodes[4] );
2905   if ( i ) {
2906     i += 4;
2907     gp_Pnt Ptmp = P[ i ];
2908     P[ i ] = P[ i+1 ];
2909     P[ i+1 ] = Ptmp;
2910   }
2911
2912   // Assure that direction of the top face normal is from the bottom face
2913   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2914   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2915   if ( Nt.Dot( upDir ) < 0 ) {
2916     DUMPSO( "Reverse top face");
2917     swap( 5, 7, idNodes, P );
2918   }
2919
2920   //   DUMPSO( "OUTPUT: ========================================");
2921   //   for ( i = 0; i < 8; i++ ) {
2922   //     float *p = ugrid->GetPoint(idNodes[i]);
2923   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2924   //   }
2925
2926   return true;
2927 }*/
2928
2929 //================================================================================
2930 /*!
2931  * \brief Return nodes linked to the given one
2932  * \param theNode - the node
2933  * \param linkedNodes - the found nodes
2934  * \param type - the type of elements to check
2935  *
2936  * Medium nodes are ignored
2937  */
2938 //================================================================================
2939
2940 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2941                                        TIDSortedElemSet &   linkedNodes,
2942                                        SMDSAbs_ElementType  type )
2943 {
2944   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2945   while ( elemIt->more() )
2946   {
2947     const SMDS_MeshElement* elem = elemIt->next();
2948     if(elem->GetType() == SMDSAbs_0DElement)
2949       continue;
2950
2951     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2952     if ( elem->GetType() == SMDSAbs_Volume )
2953     {
2954       SMDS_VolumeTool vol( elem );
2955       while ( nodeIt->more() ) {
2956         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2957         if ( theNode != n && vol.IsLinked( theNode, n ))
2958           linkedNodes.insert( n );
2959       }
2960     }
2961     else
2962     {
2963       for ( int i = 0; nodeIt->more(); ++i ) {
2964         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2965         if ( n == theNode ) {
2966           int iBefore = i - 1;
2967           int iAfter  = i + 1;
2968           if ( elem->IsQuadratic() ) {
2969             int nb = elem->NbNodes() / 2;
2970             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2971             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2972           }
2973           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2974           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2975         }
2976       }
2977     }
2978   }
2979 }
2980
2981 //=======================================================================
2982 //function : laplacianSmooth
2983 //purpose  : pulls theNode toward the center of surrounding nodes directly
2984 //           connected to that node along an element edge
2985 //=======================================================================
2986
2987 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
2988                      const Handle(Geom_Surface)&          theSurface,
2989                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2990 {
2991   // find surrounding nodes
2992
2993   TIDSortedElemSet nodeSet;
2994   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2995
2996   // compute new coodrs
2997
2998   double coord[] = { 0., 0., 0. };
2999   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3000   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3001     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3002     if ( theSurface.IsNull() ) { // smooth in 3D
3003       coord[0] += node->X();
3004       coord[1] += node->Y();
3005       coord[2] += node->Z();
3006     }
3007     else { // smooth in 2D
3008       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3009       gp_XY* uv = theUVMap[ node ];
3010       coord[0] += uv->X();
3011       coord[1] += uv->Y();
3012     }
3013   }
3014   int nbNodes = nodeSet.size();
3015   if ( !nbNodes )
3016     return;
3017   coord[0] /= nbNodes;
3018   coord[1] /= nbNodes;
3019
3020   if ( !theSurface.IsNull() ) {
3021     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3022     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3023     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3024     coord[0] = p3d.X();
3025     coord[1] = p3d.Y();
3026     coord[2] = p3d.Z();
3027   }
3028   else
3029     coord[2] /= nbNodes;
3030
3031   // move node
3032
3033   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3034 }
3035
3036 //=======================================================================
3037 //function : centroidalSmooth
3038 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3039 //           surrounding elements
3040 //=======================================================================
3041
3042 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3043                       const Handle(Geom_Surface)&          theSurface,
3044                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3045 {
3046   gp_XYZ aNewXYZ(0.,0.,0.);
3047   SMESH::Controls::Area anAreaFunc;
3048   double totalArea = 0.;
3049   int nbElems = 0;
3050
3051   // compute new XYZ
3052
3053   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3054   while ( elemIt->more() )
3055   {
3056     const SMDS_MeshElement* elem = elemIt->next();
3057     nbElems++;
3058
3059     gp_XYZ elemCenter(0.,0.,0.);
3060     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3061     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3062     int nn = elem->NbNodes();
3063     if(elem->IsQuadratic()) nn = nn/2;
3064     int i=0;
3065     //while ( itN->more() ) {
3066     while ( i<nn ) {
3067       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3068       i++;
3069       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3070       aNodePoints.push_back( aP );
3071       if ( !theSurface.IsNull() ) { // smooth in 2D
3072         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3073         gp_XY* uv = theUVMap[ aNode ];
3074         aP.SetCoord( uv->X(), uv->Y(), 0. );
3075       }
3076       elemCenter += aP;
3077     }
3078     double elemArea = anAreaFunc.GetValue( aNodePoints );
3079     totalArea += elemArea;
3080     elemCenter /= nn;
3081     aNewXYZ += elemCenter * elemArea;
3082   }
3083   aNewXYZ /= totalArea;
3084   if ( !theSurface.IsNull() ) {
3085     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3086     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3087   }
3088
3089   // move node
3090
3091   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3092 }
3093
3094 //=======================================================================
3095 //function : getClosestUV
3096 //purpose  : return UV of closest projection
3097 //=======================================================================
3098
3099 static bool getClosestUV (Extrema_GenExtPS& projector,
3100                           const gp_Pnt&     point,
3101                           gp_XY &           result)
3102 {
3103   projector.Perform( point );
3104   if ( projector.IsDone() ) {
3105     double u, v, minVal = DBL_MAX;
3106     for ( int i = projector.NbExt(); i > 0; i-- )
3107 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3108       if ( projector.SquareDistance( i ) < minVal ) {
3109         minVal = projector.SquareDistance( i );
3110 #else
3111       if ( projector.Value( i ) < minVal ) {
3112         minVal = projector.Value( i );
3113 #endif
3114         projector.Point( i ).Parameter( u, v );
3115       }
3116     result.SetCoord( u, v );
3117     return true;
3118   }
3119   return false;
3120 }
3121
3122 //=======================================================================
3123 //function : Smooth
3124 //purpose  : Smooth theElements during theNbIterations or until a worst
3125 //           element has aspect ratio <= theTgtAspectRatio.
3126 //           Aspect Ratio varies in range [1.0, inf].
3127 //           If theElements is empty, the whole mesh is smoothed.
3128 //           theFixedNodes contains additionally fixed nodes. Nodes built
3129 //           on edges and boundary nodes are always fixed.
3130 //=======================================================================
3131
3132 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3133                                set<const SMDS_MeshNode*> & theFixedNodes,
3134                                const SmoothMethod          theSmoothMethod,
3135                                const int                   theNbIterations,
3136                                double                      theTgtAspectRatio,
3137                                const bool                  the2D)
3138 {
3139   myLastCreatedElems.Clear();
3140   myLastCreatedNodes.Clear();
3141
3142   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3143
3144   if ( theTgtAspectRatio < 1.0 )
3145     theTgtAspectRatio = 1.0;
3146
3147   const double disttol = 1.e-16;
3148
3149   SMESH::Controls::AspectRatio aQualityFunc;
3150
3151   SMESHDS_Mesh* aMesh = GetMeshDS();
3152
3153   if ( theElems.empty() ) {
3154     // add all faces to theElems
3155     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3156     while ( fIt->more() ) {
3157       const SMDS_MeshElement* face = fIt->next();
3158       theElems.insert( theElems.end(), face );
3159     }
3160   }
3161   // get all face ids theElems are on
3162   set< int > faceIdSet;
3163   TIDSortedElemSet::iterator itElem;
3164   if ( the2D )
3165     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3166       int fId = FindShape( *itElem );
3167       // check that corresponding submesh exists and a shape is face
3168       if (fId &&
3169           faceIdSet.find( fId ) == faceIdSet.end() &&
3170           aMesh->MeshElements( fId )) {
3171         TopoDS_Shape F = aMesh->IndexToShape( fId );
3172         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3173           faceIdSet.insert( fId );
3174       }
3175     }
3176   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3177
3178   // ===============================================
3179   // smooth elements on each TopoDS_Face separately
3180   // ===============================================
3181
3182   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3183   for ( ; fId != faceIdSet.rend(); ++fId ) {
3184     // get face surface and submesh
3185     Handle(Geom_Surface) surface;
3186     SMESHDS_SubMesh* faceSubMesh = 0;
3187     TopoDS_Face face;
3188     double fToler2 = 0, f,l;
3189     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3190     bool isUPeriodic = false, isVPeriodic = false;
3191     if ( *fId ) {
3192       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3193       surface = BRep_Tool::Surface( face );
3194       faceSubMesh = aMesh->MeshElements( *fId );
3195       fToler2 = BRep_Tool::Tolerance( face );
3196       fToler2 *= fToler2 * 10.;
3197       isUPeriodic = surface->IsUPeriodic();
3198       if ( isUPeriodic )
3199         surface->UPeriod();
3200       isVPeriodic = surface->IsVPeriodic();
3201       if ( isVPeriodic )
3202         surface->VPeriod();
3203       surface->Bounds( u1, u2, v1, v2 );
3204     }
3205     // ---------------------------------------------------------
3206     // for elements on a face, find movable and fixed nodes and
3207     // compute UV for them
3208     // ---------------------------------------------------------
3209     bool checkBoundaryNodes = false;
3210     bool isQuadratic = false;
3211     set<const SMDS_MeshNode*> setMovableNodes;
3212     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3213     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3214     list< const SMDS_MeshElement* > elemsOnFace;
3215
3216     Extrema_GenExtPS projector;
3217     GeomAdaptor_Surface surfAdaptor;
3218     if ( !surface.IsNull() ) {
3219       surfAdaptor.Load( surface );
3220       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3221     }
3222     int nbElemOnFace = 0;
3223     itElem = theElems.begin();
3224     // loop on not yet smoothed elements: look for elems on a face
3225     while ( itElem != theElems.end() ) {
3226       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3227         break; // all elements found
3228
3229       const SMDS_MeshElement* elem = *itElem;
3230       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3231            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3232         ++itElem;
3233         continue;
3234       }
3235       elemsOnFace.push_back( elem );
3236       theElems.erase( itElem++ );
3237       nbElemOnFace++;
3238
3239       if ( !isQuadratic )
3240         isQuadratic = elem->IsQuadratic();
3241
3242       // get movable nodes of elem
3243       const SMDS_MeshNode* node;
3244       SMDS_TypeOfPosition posType;
3245       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3246       int nn = 0, nbn =  elem->NbNodes();
3247       if(elem->IsQuadratic())
3248         nbn = nbn/2;
3249       while ( nn++ < nbn ) {
3250         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3251         const SMDS_PositionPtr& pos = node->GetPosition();
3252         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3253         if (posType != SMDS_TOP_EDGE &&
3254             posType != SMDS_TOP_VERTEX &&
3255             theFixedNodes.find( node ) == theFixedNodes.end())
3256         {
3257           // check if all faces around the node are on faceSubMesh
3258           // because a node on edge may be bound to face
3259           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3260           bool all = true;
3261           if ( faceSubMesh ) {
3262             while ( eIt->more() && all ) {
3263               const SMDS_MeshElement* e = eIt->next();
3264               all = faceSubMesh->Contains( e );
3265             }
3266           }
3267           if ( all )
3268             setMovableNodes.insert( node );
3269           else
3270             checkBoundaryNodes = true;
3271         }
3272         if ( posType == SMDS_TOP_3DSPACE )
3273           checkBoundaryNodes = true;
3274       }
3275
3276       if ( surface.IsNull() )
3277         continue;
3278
3279       // get nodes to check UV
3280       list< const SMDS_MeshNode* > uvCheckNodes;
3281       itN = elem->nodesIterator();
3282       nn = 0; nbn =  elem->NbNodes();
3283       if(elem->IsQuadratic())
3284         nbn = nbn/2;
3285       while ( nn++ < nbn ) {
3286         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3287         if ( uvMap.find( node ) == uvMap.end() )
3288           uvCheckNodes.push_back( node );
3289         // add nodes of elems sharing node
3290         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3291         //         while ( eIt->more() ) {
3292         //           const SMDS_MeshElement* e = eIt->next();
3293         //           if ( e != elem ) {
3294         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3295         //             while ( nIt->more() ) {
3296         //               const SMDS_MeshNode* n =
3297         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3298         //               if ( uvMap.find( n ) == uvMap.end() )
3299         //                 uvCheckNodes.push_back( n );
3300         //             }
3301         //           }
3302         //         }
3303       }
3304       // check UV on face
3305       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3306       for ( ; n != uvCheckNodes.end(); ++n ) {
3307         node = *n;
3308         gp_XY uv( 0, 0 );
3309         const SMDS_PositionPtr& pos = node->GetPosition();
3310         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3311         // get existing UV
3312         switch ( posType ) {
3313         case SMDS_TOP_FACE: {
3314           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3315           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3316           break;
3317         }
3318         case SMDS_TOP_EDGE: {
3319           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3320           Handle(Geom2d_Curve) pcurve;
3321           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3322             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3323           if ( !pcurve.IsNull() ) {
3324             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3325             uv = pcurve->Value( u ).XY();
3326           }
3327           break;
3328         }
3329         case SMDS_TOP_VERTEX: {
3330           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3331           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3332             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3333           break;
3334         }
3335         default:;
3336         }
3337         // check existing UV
3338         bool project = true;
3339         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3340         double dist1 = DBL_MAX, dist2 = 0;
3341         if ( posType != SMDS_TOP_3DSPACE ) {
3342           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3343           project = dist1 > fToler2;
3344         }
3345         if ( project ) { // compute new UV
3346           gp_XY newUV;
3347           if ( !getClosestUV( projector, pNode, newUV )) {
3348             MESSAGE("Node Projection Failed " << node);
3349           }
3350           else {
3351             if ( isUPeriodic )
3352               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3353             if ( isVPeriodic )
3354               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3355             // check new UV
3356             if ( posType != SMDS_TOP_3DSPACE )
3357               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3358             if ( dist2 < dist1 )
3359               uv = newUV;
3360           }
3361         }
3362         // store UV in the map
3363         listUV.push_back( uv );
3364         uvMap.insert( make_pair( node, &listUV.back() ));
3365       }
3366     } // loop on not yet smoothed elements
3367
3368     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3369       checkBoundaryNodes = true;
3370
3371     // fix nodes on mesh boundary
3372
3373     if ( checkBoundaryNodes ) {
3374       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3375       map< SMESH_TLink, int >::iterator link_nb;
3376       // put all elements links to linkNbMap
3377       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3378       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3379         const SMDS_MeshElement* elem = (*elemIt);
3380         int nbn =  elem->NbCornerNodes();
3381         // loop on elem links: insert them in linkNbMap
3382         for ( int iN = 0; iN < nbn; ++iN ) {
3383           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3384           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3385           SMESH_TLink link( n1, n2 );
3386           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3387           link_nb->second++;
3388         }
3389       }
3390       // remove nodes that are in links encountered only once from setMovableNodes
3391       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3392         if ( link_nb->second == 1 ) {
3393           setMovableNodes.erase( link_nb->first.node1() );
3394           setMovableNodes.erase( link_nb->first.node2() );
3395         }
3396       }
3397     }
3398
3399     // -----------------------------------------------------
3400     // for nodes on seam edge, compute one more UV ( uvMap2 );
3401     // find movable nodes linked to nodes on seam and which
3402     // are to be smoothed using the second UV ( uvMap2 )
3403     // -----------------------------------------------------
3404
3405     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3406     if ( !surface.IsNull() ) {
3407       TopExp_Explorer eExp( face, TopAbs_EDGE );
3408       for ( ; eExp.More(); eExp.Next() ) {
3409         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3410         if ( !BRep_Tool::IsClosed( edge, face ))
3411           continue;
3412         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3413         if ( !sm ) continue;
3414         // find out which parameter varies for a node on seam
3415         double f,l;
3416         gp_Pnt2d uv1, uv2;
3417         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3418         if ( pcurve.IsNull() ) continue;
3419         uv1 = pcurve->Value( f );
3420         edge.Reverse();
3421         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3422         if ( pcurve.IsNull() ) continue;
3423         uv2 = pcurve->Value( f );
3424         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3425         // assure uv1 < uv2
3426         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3427           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3428         }
3429         // get nodes on seam and its vertices
3430         list< const SMDS_MeshNode* > seamNodes;
3431         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3432         while ( nSeamIt->more() ) {
3433           const SMDS_MeshNode* node = nSeamIt->next();
3434           if ( !isQuadratic || !IsMedium( node ))
3435             seamNodes.push_back( node );
3436         }
3437         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3438         for ( ; vExp.More(); vExp.Next() ) {
3439           sm = aMesh->MeshElements( vExp.Current() );
3440           if ( sm ) {
3441             nSeamIt = sm->GetNodes();
3442             while ( nSeamIt->more() )
3443               seamNodes.push_back( nSeamIt->next() );
3444           }
3445         }
3446         // loop on nodes on seam
3447         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3448         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3449           const SMDS_MeshNode* nSeam = *noSeIt;
3450           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3451           if ( n_uv == uvMap.end() )
3452             continue;
3453           // set the first UV
3454           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3455           // set the second UV
3456           listUV.push_back( *n_uv->second );
3457           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3458           if ( uvMap2.empty() )
3459             uvMap2 = uvMap; // copy the uvMap contents
3460           uvMap2[ nSeam ] = &listUV.back();
3461
3462           // collect movable nodes linked to ones on seam in nodesNearSeam
3463           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3464           while ( eIt->more() ) {
3465             const SMDS_MeshElement* e = eIt->next();
3466             int nbUseMap1 = 0, nbUseMap2 = 0;
3467             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3468             int nn = 0, nbn =  e->NbNodes();
3469             if(e->IsQuadratic()) nbn = nbn/2;
3470             while ( nn++ < nbn )
3471             {
3472               const SMDS_MeshNode* n =
3473                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3474               if (n == nSeam ||
3475                   setMovableNodes.find( n ) == setMovableNodes.end() )
3476                 continue;
3477               // add only nodes being closer to uv2 than to uv1
3478               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3479                            0.5 * ( n->Y() + nSeam->Y() ),
3480                            0.5 * ( n->Z() + nSeam->Z() ));
3481               gp_XY uv;
3482               getClosestUV( projector, pMid, uv );
3483               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3484                 nodesNearSeam.insert( n );
3485                 nbUseMap2++;
3486               }
3487               else
3488                 nbUseMap1++;
3489             }
3490             // for centroidalSmooth all element nodes must
3491             // be on one side of a seam
3492             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3493               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3494               nn = 0;
3495               while ( nn++ < nbn ) {
3496                 const SMDS_MeshNode* n =
3497                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3498                 setMovableNodes.erase( n );
3499               }
3500             }
3501           }
3502         } // loop on nodes on seam
3503       } // loop on edge of a face
3504     } // if ( !face.IsNull() )
3505
3506     if ( setMovableNodes.empty() ) {
3507       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3508       continue; // goto next face
3509     }
3510
3511     // -------------
3512     // SMOOTHING //
3513     // -------------
3514
3515     int it = -1;
3516     double maxRatio = -1., maxDisplacement = -1.;
3517     set<const SMDS_MeshNode*>::iterator nodeToMove;
3518     for ( it = 0; it < theNbIterations; it++ ) {
3519       maxDisplacement = 0.;
3520       nodeToMove = setMovableNodes.begin();
3521       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3522         const SMDS_MeshNode* node = (*nodeToMove);
3523         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3524
3525         // smooth
3526         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3527         if ( theSmoothMethod == LAPLACIAN )
3528           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3529         else
3530           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3531
3532         // node displacement
3533         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3534         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3535         if ( aDispl > maxDisplacement )
3536           maxDisplacement = aDispl;
3537       }
3538       // no node movement => exit
3539       //if ( maxDisplacement < 1.e-16 ) {
3540       if ( maxDisplacement < disttol ) {
3541         MESSAGE("-- no node movement --");
3542         break;
3543       }
3544
3545       // check elements quality
3546       maxRatio  = 0;
3547       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3548       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3549         const SMDS_MeshElement* elem = (*elemIt);
3550         if ( !elem || elem->GetType() != SMDSAbs_Face )
3551           continue;
3552         SMESH::Controls::TSequenceOfXYZ aPoints;
3553         if ( aQualityFunc.GetPoints( elem, aPoints )) {
3554           double aValue = aQualityFunc.GetValue( aPoints );
3555           if ( aValue > maxRatio )
3556             maxRatio = aValue;
3557         }
3558       }
3559       if ( maxRatio <= theTgtAspectRatio ) {
3560         MESSAGE("-- quality achived --");
3561         break;
3562       }
3563       if (it+1 == theNbIterations) {
3564         MESSAGE("-- Iteration limit exceeded --");
3565       }
3566     } // smoothing iterations
3567
3568     MESSAGE(" Face id: " << *fId <<
3569             " Nb iterstions: " << it <<
3570             " Displacement: " << maxDisplacement <<
3571             " Aspect Ratio " << maxRatio);
3572
3573     // ---------------------------------------
3574     // new nodes positions are computed,
3575     // record movement in DS and set new UV
3576     // ---------------------------------------
3577     nodeToMove = setMovableNodes.begin();
3578     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3579       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3580       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3581       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3582       if ( node_uv != uvMap.end() ) {
3583         gp_XY* uv = node_uv->second;
3584         node->SetPosition
3585           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3586       }
3587     }
3588
3589     // move medium nodes of quadratic elements
3590     if ( isQuadratic )
3591     {
3592       SMESH_MesherHelper helper( *GetMesh() );
3593       if ( !face.IsNull() )
3594         helper.SetSubShape( face );
3595       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3596       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3597         const SMDS_VtkFace* QF =
3598           dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3599         if(QF && QF->IsQuadratic()) {
3600           vector<const SMDS_MeshNode*> Ns;
3601           Ns.reserve(QF->NbNodes()+1);
3602           SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3603           while ( anIter->more() )
3604             Ns.push_back( cast2Node(anIter->next()) );
3605           Ns.push_back( Ns[0] );
3606           double x, y, z;
3607           for(int i=0; i<QF->NbNodes(); i=i+2) {
3608             if ( !surface.IsNull() ) {
3609               gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3610               gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3611               gp_XY uv = ( uv1 + uv2 ) / 2.;
3612               gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3613               x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3614             }
3615             else {
3616               x = (Ns[i]->X() + Ns[i+2]->X())/2;
3617               y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3618               z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3619             }
3620             if( fabs( Ns[i+1]->X() - x ) > disttol ||
3621                 fabs( Ns[i+1]->Y() - y ) > disttol ||
3622                 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3623               // we have to move i+1 node
3624               aMesh->MoveNode( Ns[i+1], x, y, z );
3625             }
3626           }
3627         }
3628       }
3629     }
3630
3631   } // loop on face ids
3632
3633 }
3634
3635 //=======================================================================
3636 //function : isReverse
3637 //purpose  : Return true if normal of prevNodes is not co-directied with
3638 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3639 //           iNotSame is where prevNodes and nextNodes are different.
3640 //           If result is true then future volume orientation is OK
3641 //=======================================================================
3642
3643 static bool isReverse(const SMDS_MeshElement*             face,
3644                       const vector<const SMDS_MeshNode*>& prevNodes,
3645                       const vector<const SMDS_MeshNode*>& nextNodes,
3646                       const int                           iNotSame)
3647 {
3648
3649   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3650   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3651   gp_XYZ extrDir( pN - pP ), faceNorm;
3652   SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3653
3654   return faceNorm * extrDir < 0.0;
3655 }
3656
3657 //=======================================================================
3658 /*!
3659  * \brief Create elements by sweeping an element
3660  * \param elem - element to sweep
3661  * \param newNodesItVec - nodes generated from each node of the element
3662  * \param newElems - generated elements
3663  * \param nbSteps - number of sweeping steps
3664  * \param srcElements - to append elem for each generated element
3665  */
3666 //=======================================================================
3667
3668 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
3669                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3670                                     list<const SMDS_MeshElement*>&        newElems,
3671                                     const int                             nbSteps,
3672                                     SMESH_SequenceOfElemPtr&              srcElements)
3673 {
3674   //MESSAGE("sweepElement " << nbSteps);
3675   SMESHDS_Mesh* aMesh = GetMeshDS();
3676
3677   const int           nbNodes = elem->NbNodes();
3678   const int         nbCorners = elem->NbCornerNodes();
3679   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3680                                                           polyhedron creation !!! */
3681   // Loop on elem nodes:
3682   // find new nodes and detect same nodes indices
3683   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3684   vector<const SMDS_MeshNode*> prevNod( nbNodes );
3685   vector<const SMDS_MeshNode*> nextNod( nbNodes );
3686   vector<const SMDS_MeshNode*> midlNod( nbNodes );
3687
3688   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3689   vector<int> sames(nbNodes);
3690   vector<bool> isSingleNode(nbNodes);
3691
3692   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3693     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
3694     const SMDS_MeshNode*                         node = nnIt->first;
3695     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3696     if ( listNewNodes.empty() )
3697       return;
3698
3699     itNN   [ iNode ] = listNewNodes.begin();
3700     prevNod[ iNode ] = node;
3701     nextNod[ iNode ] = listNewNodes.front();
3702
3703     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3704                                                              corner node of linear */
3705     if ( prevNod[ iNode ] != nextNod [ iNode ])
3706       nbDouble += !isSingleNode[iNode];
3707
3708     if( iNode < nbCorners ) { // check corners only
3709       if ( prevNod[ iNode ] == nextNod [ iNode ])
3710         sames[nbSame++] = iNode;
3711       else
3712         iNotSameNode = iNode;
3713     }
3714   }
3715
3716   if ( nbSame == nbNodes || nbSame > 2) {
3717     MESSAGE( " Too many same nodes of element " << elem->GetID() );
3718     return;
3719   }
3720
3721   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3722   {
3723     // fix nodes order to have bottom normal external
3724     if ( baseType == SMDSEntity_Polygon )
3725     {
3726       std::reverse( itNN.begin(), itNN.end() );
3727       std::reverse( prevNod.begin(), prevNod.end() );
3728       std::reverse( midlNod.begin(), midlNod.end() );
3729       std::reverse( nextNod.begin(), nextNod.end() );
3730       std::reverse( isSingleNode.begin(), isSingleNode.end() );
3731     }
3732     else
3733     {
3734       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3735       SMDS_MeshCell::applyInterlace( ind, itNN );
3736       SMDS_MeshCell::applyInterlace( ind, prevNod );
3737       SMDS_MeshCell::applyInterlace( ind, nextNod );
3738       SMDS_MeshCell::applyInterlace( ind, midlNod );
3739       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3740       if ( nbSame > 0 )
3741       {
3742         sames[nbSame] = iNotSameNode;
3743         for ( int j = 0; j <= nbSame; ++j )
3744           for ( size_t i = 0; i < ind.size(); ++i )
3745             if ( ind[i] == sames[j] )
3746             {
3747               sames[j] = i;
3748               break;
3749             }
3750         iNotSameNode = sames[nbSame];
3751       }
3752     }
3753   }
3754
3755   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3756   if ( nbSame > 0 ) {
3757     iSameNode    = sames[ nbSame-1 ];
3758     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
3759     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
3760     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
3761   }
3762
3763   // make new elements
3764   for (int iStep = 0; iStep < nbSteps; iStep++ )
3765   {
3766     // get next nodes
3767     for ( iNode = 0; iNode < nbNodes; iNode++ )
3768     {
3769       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3770       nextNod[ iNode ] = *itNN[ iNode ]++;
3771     }
3772
3773     SMDS_MeshElement* aNewElem = 0;
3774     /*if(!elem->IsPoly())*/ {
3775       switch ( baseType ) {
3776       case SMDSEntity_0D:
3777       case SMDSEntity_Node: { // sweep NODE
3778         if ( nbSame == 0 ) {
3779           if ( isSingleNode[0] )
3780             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3781           else
3782             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3783         }
3784         else
3785           return;
3786         break;
3787       }
3788       case SMDSEntity_Edge: { // sweep EDGE
3789         if ( nbDouble == 0 )
3790         {
3791           if ( nbSame == 0 ) // ---> quadrangle
3792             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3793                                       nextNod[ 1 ], nextNod[ 0 ] );
3794           else               // ---> triangle
3795             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3796                                       nextNod[ iNotSameNode ] );
3797         }
3798         else                 // ---> polygon
3799         {
3800           vector<const SMDS_MeshNode*> poly_nodes;
3801           poly_nodes.push_back( prevNod[0] );
3802           poly_nodes.push_back( prevNod[1] );
3803           if ( prevNod[1] != nextNod[1] )
3804           {
3805             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3806             poly_nodes.push_back( nextNod[1] );
3807           }
3808           if ( prevNod[0] != nextNod[0] )
3809           {
3810             poly_nodes.push_back( nextNod[0] );
3811             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3812           }
3813           switch ( poly_nodes.size() ) {
3814           case 3:
3815             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3816             break;
3817           case 4:
3818             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3819                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
3820             break;
3821           default:
3822             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3823           }
3824         }
3825         break;
3826       }
3827       case SMDSEntity_Triangle: // TRIANGLE --->
3828         {
3829           if ( nbDouble > 0 ) break;
3830           if ( nbSame == 0 )       // ---> pentahedron
3831             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3832                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3833
3834           else if ( nbSame == 1 )  // ---> pyramid
3835             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3836                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3837                                          nextNod[ iSameNode ]);
3838
3839           else // 2 same nodes:       ---> tetrahedron
3840             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3841                                          nextNod[ iNotSameNode ]);
3842           break;
3843         }
3844       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3845         {
3846           if ( nbSame == 2 )
3847             return;
3848           if ( nbDouble+nbSame == 2 )
3849           {
3850             if(nbSame==0) {      // ---> quadratic quadrangle
3851               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3852                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3853             }
3854             else { //(nbSame==1) // ---> quadratic triangle
3855               if(sames[0]==2) {
3856                 return; // medium node on axis
3857               }
3858               else if(sames[0]==0)
3859                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3860                                           nextNod[2], midlNod[1], prevNod[2]);
3861               else // sames[0]==1
3862                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3863                                           midlNod[0], nextNod[2], prevNod[2]);
3864             }
3865           }
3866           else if ( nbDouble == 3 )
3867           {
3868             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
3869               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3870                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3871             }
3872           }
3873           else
3874             return;
3875           break;
3876         }
3877       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3878         if ( nbDouble > 0 ) break;
3879
3880         if ( nbSame == 0 )       // ---> hexahedron
3881           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3882                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3883
3884         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3885           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3886                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
3887                                        nextNod[ iSameNode ]);
3888           newElems.push_back( aNewElem );
3889           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
3890                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3891                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
3892         }
3893         else if ( nbSame == 2 ) { // ---> pentahedron
3894           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3895             // iBeforeSame is same too
3896             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3897                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
3898                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
3899           else
3900             // iAfterSame is same too
3901             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
3902                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3903                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
3904         }
3905         break;
3906       }
3907       case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3908         if ( nbDouble+nbSame != 3 ) break;
3909         if(nbSame==0) {
3910           // --->  pentahedron with 15 nodes
3911           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3912                                        nextNod[0], nextNod[1], nextNod[2],
3913                                        prevNod[3], prevNod[4], prevNod[5],
3914                                        nextNod[3], nextNod[4], nextNod[5],
3915                                        midlNod[0], midlNod[1], midlNod[2]);
3916         }
3917         else if(nbSame==1) {
3918           // --->  2d order pyramid of 13 nodes
3919           int apex = iSameNode;
3920           int i0 = ( apex + 1 ) % nbCorners;
3921           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3922           int i0a = apex + 3;
3923           int i1a = i1 + 3;
3924           int i01 = i0 + 3;
3925           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3926                                       nextNod[i0], nextNod[i1], prevNod[apex],
3927                                       prevNod[i01], midlNod[i0],
3928                                       nextNod[i01], midlNod[i1],
3929                                       prevNod[i1a], prevNod[i0a],
3930                                       nextNod[i0a], nextNod[i1a]);
3931         }
3932         else if(nbSame==2) {
3933           // --->  2d order tetrahedron of 10 nodes
3934           int n1 = iNotSameNode;
3935           int n2 = ( n1 + 1             ) % nbCorners;
3936           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3937           int n12 = n1 + 3;
3938           int n23 = n2 + 3;
3939           int n31 = n3 + 3;
3940           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3941                                        prevNod[n12], prevNod[n23], prevNod[n31],
3942                                        midlNod[n1], nextNod[n12], nextNod[n31]);
3943         }
3944         break;
3945       }
3946       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3947         if( nbSame == 0 ) {
3948           if ( nbDouble != 4 ) break;
3949           // --->  hexahedron with 20 nodes
3950           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3951                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3952                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3953                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3954                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3955         }
3956         else if(nbSame==1) {
3957           // ---> pyramid + pentahedron - can not be created since it is needed
3958           // additional middle node at the center of face
3959           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3960           return;
3961         }
3962         else if( nbSame == 2 ) {
3963           if ( nbDouble != 2 ) break;
3964           // --->  2d order Pentahedron with 15 nodes
3965           int n1,n2,n4,n5;
3966           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3967             // iBeforeSame is same too
3968             n1 = iBeforeSame;
3969             n2 = iOpposSame;
3970             n4 = iSameNode;
3971             n5 = iAfterSame;
3972           }
3973           else {
3974             // iAfterSame is same too
3975             n1 = iSameNode;
3976             n2 = iBeforeSame;
3977             n4 = iAfterSame;
3978             n5 = iOpposSame;
3979           }
3980           int n12 = n2 + 4;
3981           int n45 = n4 + 4;
3982           int n14 = n1 + 4;
3983           int n25 = n5 + 4;
3984           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3985                                        prevNod[n4], prevNod[n5], nextNod[n5],
3986                                        prevNod[n12], midlNod[n2], nextNod[n12],
3987                                        prevNod[n45], midlNod[n5], nextNod[n45],
3988                                        prevNod[n14], prevNod[n25], nextNod[n25]);
3989         }
3990         break;
3991       }
3992       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3993
3994         if( nbSame == 0 && nbDouble == 9 ) {
3995           // --->  tri-quadratic hexahedron with 27 nodes
3996           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3997                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3998                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3999                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4000                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4001                                        prevNod[8], // bottom center
4002                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4003                                        nextNod[8], // top center
4004                                        midlNod[8]);// elem center
4005         }
4006         else
4007         {
4008           return;
4009         }
4010         break;
4011       }
4012       case SMDSEntity_Polygon: { // sweep POLYGON
4013
4014         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4015           // --->  hexagonal prism
4016           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4017                                        prevNod[3], prevNod[4], prevNod[5],
4018                                        nextNod[0], nextNod[1], nextNod[2],
4019                                        nextNod[3], nextNod[4], nextNod[5]);
4020         }
4021         break;
4022       }
4023       case SMDSEntity_Ball:
4024         return;
4025
4026       default:
4027         break;
4028       }
4029     }
4030
4031     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4032     {
4033       if ( baseType != SMDSEntity_Polygon )
4034       {
4035         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4036         SMDS_MeshCell::applyInterlace( ind, prevNod );
4037         SMDS_MeshCell::applyInterlace( ind, nextNod );
4038         SMDS_MeshCell::applyInterlace( ind, midlNod );
4039         SMDS_MeshCell::applyInterlace( ind, itNN );
4040         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4041         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4042       }
4043       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4044       vector<int> quantities (nbNodes + 2);
4045       polyedre_nodes.clear();
4046       quantities.clear();
4047
4048       // bottom of prism
4049       for (int inode = 0; inode < nbNodes; inode++)
4050         polyedre_nodes.push_back( prevNod[inode] );
4051       quantities.push_back( nbNodes );
4052
4053       // top of prism
4054       polyedre_nodes.push_back( nextNod[0] );
4055       for (int inode = nbNodes; inode-1; --inode )
4056         polyedre_nodes.push_back( nextNod[inode-1] );
4057       quantities.push_back( nbNodes );
4058
4059       // side faces
4060       for (int iface = 0; iface < nbNodes; iface++)
4061       {
4062         const int prevNbNodes = polyedre_nodes.size();
4063         int inextface = (iface+1) % nbNodes;
4064         polyedre_nodes.push_back( prevNod[inextface] );
4065         polyedre_nodes.push_back( prevNod[iface] );
4066         if ( prevNod[iface] != nextNod[iface] )
4067         {
4068           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4069           polyedre_nodes.push_back( nextNod[iface] );
4070         }
4071         if ( prevNod[inextface] != nextNod[inextface] )
4072         {
4073           polyedre_nodes.push_back( nextNod[inextface] );
4074           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4075         }
4076         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4077         if ( nbFaceNodes > 2 )
4078           quantities.push_back( nbFaceNodes );
4079         else // degenerated face
4080           polyedre_nodes.resize( prevNbNodes );
4081       }
4082       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4083     }
4084
4085     if ( aNewElem ) {
4086       newElems.push_back( aNewElem );
4087       myLastCreatedElems.Append(aNewElem);
4088       srcElements.Append( elem );
4089     }
4090
4091     // set new prev nodes
4092     for ( iNode = 0; iNode < nbNodes; iNode++ )
4093       prevNod[ iNode ] = nextNod[ iNode ];
4094
4095   } // for steps
4096 }
4097
4098 //=======================================================================
4099 /*!
4100  * \brief Create 1D and 2D elements around swept elements
4101  * \param mapNewNodes - source nodes and ones generated from them
4102  * \param newElemsMap - source elements and ones generated from them
4103  * \param elemNewNodesMap - nodes generated from each node of each element
4104  * \param elemSet - all swept elements
4105  * \param nbSteps - number of sweeping steps
4106  * \param srcElements - to append elem for each generated element
4107  */
4108 //=======================================================================
4109
4110 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4111                                   TElemOfElemListMap &     newElemsMap,
4112                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4113                                   TIDSortedElemSet&        elemSet,
4114                                   const int                nbSteps,
4115                                   SMESH_SequenceOfElemPtr& srcElements)
4116 {
4117   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4118   SMESHDS_Mesh* aMesh = GetMeshDS();
4119
4120   // Find nodes belonging to only one initial element - sweep them to get edges.
4121
4122   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4123   for ( ; nList != mapNewNodes.end(); nList++ )
4124   {
4125     const SMDS_MeshNode* node =
4126       static_cast<const SMDS_MeshNode*>( nList->first );
4127     if ( newElemsMap.count( node ))
4128       continue; // node was extruded into edge
4129     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4130     int nbInitElems = 0;
4131     const SMDS_MeshElement* el = 0;
4132     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4133     while ( eIt->more() && nbInitElems < 2 ) {
4134       el = eIt->next();
4135       SMDSAbs_ElementType type = el->GetType();
4136       if ( type == SMDSAbs_Volume || type < highType ) continue;
4137       if ( type > highType ) {
4138         nbInitElems = 0;
4139         highType = type;
4140       }
4141       nbInitElems += elemSet.count(el);
4142     }
4143     if ( nbInitElems < 2 ) {
4144       bool NotCreateEdge = el && el->IsMediumNode(node);
4145       if(!NotCreateEdge) {
4146         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4147         list<const SMDS_MeshElement*> newEdges;
4148         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4149       }
4150     }
4151   }
4152
4153   // Make a ceiling for each element ie an equal element of last new nodes.
4154   // Find free links of faces - make edges and sweep them into faces.
4155
4156   TElemOfElemListMap::iterator   itElem      = newElemsMap.begin();
4157   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4158   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4159   {
4160     const SMDS_MeshElement* elem = itElem->first;
4161     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4162
4163     if(itElem->second.size()==0) continue;
4164
4165     const bool isQuadratic = elem->IsQuadratic();
4166
4167     if ( elem->GetType() == SMDSAbs_Edge ) {
4168       // create a ceiling edge
4169       if ( !isQuadratic ) {
4170         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4171                                vecNewNodes[ 1 ]->second.back())) {
4172           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4173                                                    vecNewNodes[ 1 ]->second.back()));
4174           srcElements.Append( elem );
4175         }
4176       }
4177       else {
4178         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4179                                vecNewNodes[ 1 ]->second.back(),
4180                                vecNewNodes[ 2 ]->second.back())) {
4181           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4182                                                    vecNewNodes[ 1 ]->second.back(),
4183                                                    vecNewNodes[ 2 ]->second.back()));
4184           srcElements.Append( elem );
4185         }
4186       }
4187     }
4188     if ( elem->GetType() != SMDSAbs_Face )
4189       continue;
4190
4191     bool hasFreeLinks = false;
4192
4193     TIDSortedElemSet avoidSet;
4194     avoidSet.insert( elem );
4195
4196     set<const SMDS_MeshNode*> aFaceLastNodes;
4197     int iNode, nbNodes = vecNewNodes.size();
4198     if ( !isQuadratic ) {
4199       // loop on the face nodes
4200       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4202         // look for free links of the face
4203         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4204         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4205         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4206         // check if a link n1-n2 is free
4207         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4208           hasFreeLinks = true;
4209           // make a new edge and a ceiling for a new edge
4210           const SMDS_MeshElement* edge;
4211           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4212             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4213             srcElements.Append( myLastCreatedElems.Last() );
4214           }
4215           n1 = vecNewNodes[ iNode ]->second.back();
4216           n2 = vecNewNodes[ iNext ]->second.back();
4217           if ( !aMesh->FindEdge( n1, n2 )) {
4218             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4219             srcElements.Append( edge );
4220           }
4221         }
4222       }
4223     }
4224     else { // elem is quadratic face
4225       int nbn = nbNodes/2;
4226       for ( iNode = 0; iNode < nbn; iNode++ ) {
4227         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4228         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4229         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4230         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4231         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4232         // check if a link is free
4233         if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4234              ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4235              ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4236           hasFreeLinks = true;
4237           // make an edge and a ceiling for a new edge
4238           // find medium node
4239           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4240             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4241             srcElements.Append( elem );
4242           }
4243           n1 = vecNewNodes[ iNode ]->second.back();
4244           n2 = vecNewNodes[ iNext ]->second.back();
4245           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4246           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4247             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4248             srcElements.Append( elem );
4249           }
4250         }
4251       }
4252       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4253         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4254       }
4255     }
4256
4257     // sweep free links into faces
4258
4259     if ( hasFreeLinks )  {
4260       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4261       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4262
4263       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4264       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4265         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4266         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4267       }
4268       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4269         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4270         std::advance( v, volNb );
4271         // find indices of free faces of a volume and their source edges
4272         list< int > freeInd;
4273         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4274         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4275         int iF, nbF = vTool.NbFaces();
4276         for ( iF = 0; iF < nbF; iF ++ ) {
4277           if (vTool.IsFreeFace( iF ) &&
4278               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4279               initNodeSet != faceNodeSet) // except an initial face
4280           {
4281             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4282               continue;
4283             freeInd.push_back( iF );
4284             // find source edge of a free face iF
4285             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4286             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4287             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4288                                    initNodeSet.begin(), initNodeSet.end(),
4289                                    commonNodes.begin());
4290             if ( (*v)->IsQuadratic() )
4291               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4292             else
4293               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4294 #ifdef _DEBUG_
4295             if ( !srcEdges.back() )
4296             {
4297               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4298                    << iF << " of volume #" << vTool.ID() << endl;
4299             }
4300 #endif
4301           }
4302         }
4303         if ( freeInd.empty() )
4304           continue;
4305
4306         // create faces for all steps;
4307         // if such a face has been already created by sweep of edge,
4308         // assure that its orientation is OK
4309         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4310           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4311           vTool.SetExternalNormal();
4312           const int nextShift = vTool.IsForward() ? +1 : -1;
4313           list< int >::iterator ind = freeInd.begin();
4314           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4315           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4316           {
4317             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4318             int nbn = vTool.NbFaceNodes( *ind );
4319             const SMDS_MeshElement * f = 0;
4320             if ( nbn == 3 )              ///// triangle
4321             {
4322               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4323               if ( !f ||
4324                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4325               {
4326                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4327                                                      nodes[ 1 ],
4328                                                      nodes[ 1 + nextShift ] };
4329                 if ( f )
4330                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4331                 else
4332                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4333                                                             newOrder[ 2 ] ));
4334               }
4335             }
4336             else if ( nbn == 4 )       ///// quadrangle
4337             {
4338               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4339               if ( !f ||
4340                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4341               {
4342                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4343                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4344                 if ( f )
4345                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4346                 else
4347                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4348                                                             newOrder[ 2 ], newOrder[ 3 ]));
4349               }
4350             }
4351             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4352             {
4353               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4354               if ( !f ||
4355                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4356               {
4357                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4358                                                      nodes[2],
4359                                                      nodes[2 + 2*nextShift],
4360                                                      nodes[3 - 2*nextShift],
4361                                                      nodes[3],
4362                                                      nodes[3 + 2*nextShift]};
4363                 if ( f )
4364                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4365                 else
4366                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4367                                                             newOrder[ 1 ],
4368                                                             newOrder[ 2 ],
4369                                                             newOrder[ 3 ],
4370                                                             newOrder[ 4 ],
4371                                                             newOrder[ 5 ] ));
4372               }
4373             }
4374             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4375             {
4376               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4377                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4378               if ( !f ||
4379                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4380               {
4381                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4382                                                      nodes[4 - 2*nextShift],
4383                                                      nodes[4],
4384                                                      nodes[4 + 2*nextShift],
4385                                                      nodes[1],
4386                                                      nodes[5 - 2*nextShift],
4387                                                      nodes[5],
4388                                                      nodes[5 + 2*nextShift] };
4389                 if ( f )
4390                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4391                 else
4392                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4393                                                            newOrder[ 2 ], newOrder[ 3 ],
4394                                                            newOrder[ 4 ], newOrder[ 5 ],
4395                                                            newOrder[ 6 ], newOrder[ 7 ]));
4396               }
4397             }
4398             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4399             {
4400               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4401                                       SMDSAbs_Face, /*noMedium=*/false);
4402               if ( !f ||
4403                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4404               {
4405                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4406                                                      nodes[4 - 2*nextShift],
4407                                                      nodes[4],
4408                                                      nodes[4 + 2*nextShift],
4409                                                      nodes[1],
4410                                                      nodes[5 - 2*nextShift],
4411                                                      nodes[5],
4412                                                      nodes[5 + 2*nextShift],
4413                                                      nodes[8] };
4414                 if ( f )
4415                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4416                 else
4417                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4418                                                            newOrder[ 2 ], newOrder[ 3 ],
4419                                                            newOrder[ 4 ], newOrder[ 5 ],
4420                                                            newOrder[ 6 ], newOrder[ 7 ],
4421                                                            newOrder[ 8 ]));
4422               }
4423             }
4424             else  //////// polygon
4425             {
4426               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4427               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4428               if ( !f ||
4429                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4430               {
4431                 if ( !vTool.IsForward() )
4432                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4433                 if ( f )
4434                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4435                 else
4436                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4437               }
4438             }
4439
4440             while ( srcElements.Length() < myLastCreatedElems.Length() )
4441               srcElements.Append( *srcEdge );
4442
4443           }  // loop on free faces
4444
4445           // go to the next volume
4446           iVol = 0;
4447           while ( iVol++ < nbVolumesByStep ) v++;
4448
4449         } // loop on steps
4450       } // loop on volumes of one step
4451     } // sweep free links into faces
4452
4453     // Make a ceiling face with a normal external to a volume
4454
4455     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4456
4457     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4458     if ( iF >= 0 ) {
4459       lastVol.SetExternalNormal();
4460       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4461       int nbn = lastVol.NbFaceNodes( iF );
4462       if ( nbn == 3 ) {
4463         if (!hasFreeLinks ||
4464             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4465           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4466       }
4467       else if ( nbn == 4 )
4468       {
4469         if (!hasFreeLinks ||
4470             !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4471           myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4472       }
4473       else if ( nbn == 6 && isQuadratic )
4474       {
4475         if (!hasFreeLinks ||
4476             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4477           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4478                                                    nodes[1], nodes[3], nodes[5]));
4479       }
4480       else if ( nbn == 8 && isQuadratic )
4481       {
4482         if (!hasFreeLinks ||
4483             !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4484                              nodes[1], nodes[3], nodes[5], nodes[7]) )
4485           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4486                                                    nodes[1], nodes[3], nodes[5], nodes[7]));
4487       }
4488       else if ( nbn == 9 && isQuadratic )
4489       {
4490         if (!hasFreeLinks ||
4491             !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4492                                 SMDSAbs_Face, /*noMedium=*/false) )
4493           myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4494                                                    nodes[1], nodes[3], nodes[5], nodes[7],
4495                                                    nodes[8]));
4496       }
4497       else {
4498         vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4499         if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4500           myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4501       }
4502
4503       while ( srcElements.Length() < myLastCreatedElems.Length() )
4504         srcElements.Append( elem );
4505     }
4506   } // loop on swept elements
4507 }
4508
4509 //=======================================================================
4510 //function : RotationSweep
4511 //purpose  :
4512 //=======================================================================
4513
4514 SMESH_MeshEditor::PGroupIDs
4515 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4516                                 const gp_Ax1&      theAxis,
4517                                 const double       theAngle,
4518                                 const int          theNbSteps,
4519                                 const double       theTol,
4520                                 const bool         theMakeGroups,
4521                                 const bool         theMakeWalls)
4522 {
4523   myLastCreatedElems.Clear();
4524   myLastCreatedNodes.Clear();
4525
4526   // source elements for each generated one
4527   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4528
4529   MESSAGE( "RotationSweep()");
4530   gp_Trsf aTrsf;
4531   aTrsf.SetRotation( theAxis, theAngle );
4532   gp_Trsf aTrsf2;
4533   aTrsf2.SetRotation( theAxis, theAngle/2. );
4534
4535   gp_Lin aLine( theAxis );
4536   double aSqTol = theTol * theTol;
4537
4538   SMESHDS_Mesh* aMesh = GetMeshDS();
4539
4540   TNodeOfNodeListMap mapNewNodes;
4541   TElemOfVecOfNnlmiMap mapElemNewNodes;
4542   TElemOfElemListMap newElemsMap;
4543
4544   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4545                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4546                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4547   // loop on theElems
4548   TIDSortedElemSet::iterator itElem;
4549   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4550     const SMDS_MeshElement* elem = *itElem;
4551     if ( !elem || elem->GetType() == SMDSAbs_Volume )
4552       continue;
4553     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4554     newNodesItVec.reserve( elem->NbNodes() );
4555
4556     // loop on elem nodes
4557     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4558     while ( itN->more() )
4559     {
4560       // check if a node has been already sweeped
4561       const SMDS_MeshNode* node = cast2Node( itN->next() );
4562
4563       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4564       double coord[3];
4565       aXYZ.Coord( coord[0], coord[1], coord[2] );
4566       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4567
4568       TNodeOfNodeListMapItr nIt =
4569         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4570       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4571       if ( listNewNodes.empty() )
4572       {
4573         // check if we are to create medium nodes between corner ones
4574         bool needMediumNodes = false;
4575         if ( isQuadraticMesh )
4576         {
4577           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4578           while (it->more() && !needMediumNodes )
4579           {
4580             const SMDS_MeshElement* invElem = it->next();
4581             if ( invElem != elem && !theElems.count( invElem )) continue;
4582             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4583             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4584               needMediumNodes = true;
4585           }
4586         }
4587
4588         // make new nodes
4589         const SMDS_MeshNode * newNode = node;
4590         for ( int i = 0; i < theNbSteps; i++ ) {
4591           if ( !isOnAxis ) {
4592             if ( needMediumNodes )  // create a medium node
4593             {
4594               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4595               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4596               myLastCreatedNodes.Append(newNode);
4597               srcNodes.Append( node );
4598               listNewNodes.push_back( newNode );
4599               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4600             }
4601             else {
4602               aTrsf.Transforms( coord[0], coord[1], coord[2] );
4603             }
4604             // create a corner node
4605             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4606             myLastCreatedNodes.Append(newNode);
4607             srcNodes.Append( node );
4608             listNewNodes.push_back( newNode );
4609           }
4610           else {
4611             listNewNodes.push_back( newNode );
4612             // if ( needMediumNodes )
4613             //   listNewNodes.push_back( newNode );
4614           }
4615         }
4616       }
4617       newNodesItVec.push_back( nIt );
4618     }
4619     // make new elements
4620     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4621   }
4622
4623   if ( theMakeWalls )
4624     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4625
4626   PGroupIDs newGroupIDs;
4627   if ( theMakeGroups )
4628     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4629
4630   return newGroupIDs;
4631 }
4632
4633
4634 //=======================================================================
4635 //function : CreateNode
4636 //purpose  :
4637 //=======================================================================
4638 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4639                                                   const double y,
4640                                                   const double z,
4641                                                   const double tolnode,
4642                                                   SMESH_SequenceOfNode& aNodes)
4643 {
4644   // myLastCreatedElems.Clear();
4645   // myLastCreatedNodes.Clear();
4646
4647   gp_Pnt P1(x,y,z);
4648   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4649
4650   // try to search in sequence of existing nodes
4651   // if aNodes.Length()>0 we 'nave to use given sequence
4652   // else - use all nodes of mesh
4653   if(aNodes.Length()>0) {
4654     int i;
4655     for(i=1; i<=aNodes.Length(); i++) {
4656       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4657       if(P1.Distance(P2)<tolnode)
4658         return aNodes.Value(i);
4659     }
4660   }
4661   else {
4662     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4663     while(itn->more()) {
4664       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4665       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4666       if(P1.Distance(P2)<tolnode)
4667         return aN;
4668     }
4669   }
4670
4671   // create new node and return it
4672   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4673   //myLastCreatedNodes.Append(NewNode);
4674   return NewNode;
4675 }
4676
4677
4678 //=======================================================================
4679 //function : ExtrusionSweep
4680 //purpose  :
4681 //=======================================================================
4682
4683 SMESH_MeshEditor::PGroupIDs
4684 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4685                                   const gp_Vec&       theStep,
4686                                   const int           theNbSteps,
4687                                   TElemOfElemListMap& newElemsMap,
4688                                   const bool          theMakeGroups,
4689                                   const int           theFlags,
4690                                   const double        theTolerance)
4691 {
4692   ExtrusParam aParams;
4693   aParams.myDir = gp_Dir(theStep);
4694   aParams.myNodes.Clear();
4695   aParams.mySteps = new TColStd_HSequenceOfReal;
4696   int i;
4697   for(i=1; i<=theNbSteps; i++)
4698     aParams.mySteps->Append(theStep.Magnitude());
4699
4700   return
4701     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4702 }
4703
4704
4705 //=======================================================================
4706 //function : ExtrusionSweep
4707 //purpose  :
4708 //=======================================================================
4709
4710 SMESH_MeshEditor::PGroupIDs
4711 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &  theElems,
4712                                   ExtrusParam&        theParams,
4713                                   TElemOfElemListMap& newElemsMap,
4714                                   const bool          theMakeGroups,
4715                                   const int           theFlags,
4716                                   const double        theTolerance)
4717 {
4718   myLastCreatedElems.Clear();
4719   myLastCreatedNodes.Clear();
4720
4721   // source elements for each generated one
4722   SMESH_SequenceOfElemPtr srcElems, srcNodes;
4723
4724   SMESHDS_Mesh* aMesh = GetMeshDS();
4725
4726   int nbsteps = theParams.mySteps->Length();
4727
4728   TNodeOfNodeListMap mapNewNodes;
4729   //TNodeOfNodeVecMap mapNewNodes;
4730   TElemOfVecOfNnlmiMap mapElemNewNodes;
4731   //TElemOfVecOfMapNodesMap mapElemNewNodes;
4732
4733   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4734                                      myMesh->NbFaces(ORDER_QUADRATIC) +
4735                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
4736   // loop on theElems
4737   TIDSortedElemSet::iterator itElem;
4738   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4739     // check element type
4740     const SMDS_MeshElement* elem = *itElem;
4741     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
4742       continue;
4743
4744     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4745     newNodesItVec.reserve( elem->NbNodes() );
4746
4747     // loop on elem nodes
4748     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4749     while ( itN->more() )
4750     {
4751       // check if a node has been already sweeped
4752       const SMDS_MeshNode* node = cast2Node( itN->next() );
4753       TNodeOfNodeListMap::iterator nIt =
4754         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4755       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4756       if ( listNewNodes.empty() )
4757       {
4758         // make new nodes
4759
4760         // check if we are to create medium nodes between corner ones
4761         bool needMediumNodes = false;
4762         if ( isQuadraticMesh )
4763         {
4764           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4765           while (it->more() && !needMediumNodes )
4766           {
4767             const SMDS_MeshElement* invElem = it->next();
4768             if ( invElem != elem && !theElems.count( invElem )) continue;
4769             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4770             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4771               needMediumNodes = true;
4772           }
4773         }
4774
4775         double coord[] = { node->X(), node->Y(), node->Z() };
4776         for ( int i = 0; i < nbsteps; i++ )
4777         {
4778           if ( needMediumNodes ) // create a medium node
4779           {
4780             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4781             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4782             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4783             if( theFlags & EXTRUSION_FLAG_SEW ) {
4784               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4785                                                          theTolerance, theParams.myNodes);
4786               listNewNodes.push_back( newNode );
4787             }
4788             else {
4789               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4790               myLastCreatedNodes.Append(newNode);
4791               srcNodes.Append( node );
4792               listNewNodes.push_back( newNode );
4793             }
4794           }
4795           // create a corner node
4796           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4797           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4798           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4799           if( theFlags & EXTRUSION_FLAG_SEW ) {
4800             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4801                                                        theTolerance, theParams.myNodes);
4802             listNewNodes.push_back( newNode );
4803           }
4804           else {
4805             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4806             myLastCreatedNodes.Append(newNode);
4807             srcNodes.Append( node );
4808             listNewNodes.push_back( newNode );
4809           }
4810         }
4811       }
4812       newNodesItVec.push_back( nIt );
4813     }
4814     // make new elements
4815     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4816   }
4817
4818   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4819     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4820   }
4821   PGroupIDs newGroupIDs;
4822   if ( theMakeGroups )
4823     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4824
4825   return newGroupIDs;
4826 }
4827
4828 //=======================================================================
4829 //function : ExtrusionAlongTrack
4830 //purpose  :
4831 //=======================================================================
4832 SMESH_MeshEditor::Extrusion_Error
4833 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
4834                                        SMESH_subMesh*       theTrack,
4835                                        const SMDS_MeshNode* theN1,
4836                                        const bool           theHasAngles,
4837                                        list<double>&        theAngles,
4838                                        const bool           theLinearVariation,
4839                                        const bool           theHasRefPoint,
4840                                        const gp_Pnt&        theRefPoint,
4841                                        const bool           theMakeGroups)
4842 {
4843   MESSAGE("ExtrusionAlongTrack");
4844   myLastCreatedElems.Clear();
4845   myLastCreatedNodes.Clear();
4846
4847   int aNbE;
4848   std::list<double> aPrms;
4849   TIDSortedElemSet::iterator itElem;
4850
4851   gp_XYZ aGC;
4852   TopoDS_Edge aTrackEdge;
4853   TopoDS_Vertex aV1, aV2;
4854
4855   SMDS_ElemIteratorPtr aItE;
4856   SMDS_NodeIteratorPtr aItN;
4857   SMDSAbs_ElementType aTypeE;
4858
4859   TNodeOfNodeListMap mapNewNodes;
4860
4861   // 1. Check data
4862   aNbE = theElements.size();
4863   // nothing to do
4864   if ( !aNbE )
4865     return EXTR_NO_ELEMENTS;
4866
4867   // 1.1 Track Pattern
4868   ASSERT( theTrack );
4869
4870   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4871
4872   aItE = pSubMeshDS->GetElements();
4873   while ( aItE->more() ) {
4874     const SMDS_MeshElement* pE = aItE->next();
4875     aTypeE = pE->GetType();
4876     // Pattern must contain links only
4877     if ( aTypeE != SMDSAbs_Edge )
4878       return EXTR_PATH_NOT_EDGE;
4879   }
4880
4881   list<SMESH_MeshEditor_PathPoint> fullList;
4882
4883   const TopoDS_Shape& aS = theTrack->GetSubShape();
4884   // Sub-shape for the Pattern must be an Edge or Wire
4885   if( aS.ShapeType() == TopAbs_EDGE ) {
4886     aTrackEdge = TopoDS::Edge( aS );
4887     // the Edge must not be degenerated
4888     if ( BRep_Tool::Degenerated( aTrackEdge ) )
4889       return EXTR_BAD_PATH_SHAPE;
4890     TopExp::Vertices( aTrackEdge, aV1, aV2 );
4891     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4892     const SMDS_MeshNode* aN1 = aItN->next();
4893     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4894     const SMDS_MeshNode* aN2 = aItN->next();
4895     // starting node must be aN1 or aN2
4896     if ( !( aN1 == theN1 || aN2 == theN1 ) )
4897       return EXTR_BAD_STARTING_NODE;
4898     aItN = pSubMeshDS->GetNodes();
4899     while ( aItN->more() ) {
4900       const SMDS_MeshNode* pNode = aItN->next();
4901       const SMDS_EdgePosition* pEPos =
4902         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4903       double aT = pEPos->GetUParameter();
4904       aPrms.push_back( aT );
4905     }
4906     //Extrusion_Error err =
4907     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4908   } else if( aS.ShapeType() == TopAbs_WIRE ) {
4909     list< SMESH_subMesh* > LSM;
4910     TopTools_SequenceOfShape Edges;
4911     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4912     while(itSM->more()) {
4913       SMESH_subMesh* SM = itSM->next();
4914       LSM.push_back(SM);
4915       const TopoDS_Shape& aS = SM->GetSubShape();
4916       Edges.Append(aS);
4917     }
4918     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4919     int startNid = theN1->GetID();
4920     TColStd_MapOfInteger UsedNums;
4921
4922     int NbEdges = Edges.Length();
4923     int i = 1;
4924     for(; i<=NbEdges; i++) {
4925       int k = 0;
4926       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4927       for(; itLSM!=LSM.end(); itLSM++) {
4928         k++;
4929         if(UsedNums.Contains(k)) continue;
4930         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4931         SMESH_subMesh* locTrack = *itLSM;
4932         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4933         TopExp::Vertices( aTrackEdge, aV1, aV2 );
4934         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4935         const SMDS_MeshNode* aN1 = aItN->next();
4936         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4937         const SMDS_MeshNode* aN2 = aItN->next();
4938         // starting node must be aN1 or aN2
4939         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4940         // 2. Collect parameters on the track edge
4941         aPrms.clear();
4942         aItN = locMeshDS->GetNodes();
4943         while ( aItN->more() ) {
4944           const SMDS_MeshNode* pNode = aItN->next();
4945           const SMDS_EdgePosition* pEPos =
4946             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4947           double aT = pEPos->GetUParameter();
4948           aPrms.push_back( aT );
4949         }
4950         list<SMESH_MeshEditor_PathPoint> LPP;
4951         //Extrusion_Error err =
4952         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4953         LLPPs.push_back(LPP);
4954         UsedNums.Add(k);
4955         // update startN for search following egde
4956         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4957         else startNid = aN1->GetID();
4958         break;
4959       }
4960     }
4961     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4962     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4963     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4964     for(; itPP!=firstList.end(); itPP++) {
4965       fullList.push_back( *itPP );
4966     }
4967     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4968     fullList.pop_back();
4969     itLLPP++;
4970     for(; itLLPP!=LLPPs.end(); itLLPP++) {
4971       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4972       itPP = currList.begin();
4973       SMESH_MeshEditor_PathPoint PP2 = currList.front();
4974       gp_Dir D1 = PP1.Tangent();
4975       gp_Dir D2 = PP2.Tangent();
4976       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4977                            (D1.Z()+D2.Z())/2 ) );
4978       PP1.SetTangent(Dnew);
4979       fullList.push_back(PP1);
4980       itPP++;
4981       for(; itPP!=firstList.end(); itPP++) {
4982         fullList.push_back( *itPP );
4983       }
4984       PP1 = fullList.back();
4985       fullList.pop_back();
4986     }
4987     // if wire not closed
4988     fullList.push_back(PP1);
4989     // else ???
4990   }
4991   else {
4992     return EXTR_BAD_PATH_SHAPE;
4993   }
4994
4995   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4996                           theHasRefPoint, theRefPoint, theMakeGroups);
4997 }
4998
4999
5000 //=======================================================================
5001 //function : ExtrusionAlongTrack
5002 //purpose  :
5003 //=======================================================================
5004 SMESH_MeshEditor::Extrusion_Error
5005 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5006                                        SMESH_Mesh*          theTrack,
5007                                        const SMDS_MeshNode* theN1,
5008                                        const bool           theHasAngles,
5009                                        list<double>&        theAngles,
5010                                        const bool           theLinearVariation,
5011                                        const bool           theHasRefPoint,
5012                                        const gp_Pnt&        theRefPoint,
5013                                        const bool           theMakeGroups)
5014 {
5015   myLastCreatedElems.Clear();
5016   myLastCreatedNodes.Clear();
5017
5018   int aNbE;
5019   std::list<double> aPrms;
5020   TIDSortedElemSet::iterator itElem;
5021
5022   gp_XYZ aGC;
5023   TopoDS_Edge aTrackEdge;
5024   TopoDS_Vertex aV1, aV2;
5025
5026   SMDS_ElemIteratorPtr aItE;
5027   SMDS_NodeIteratorPtr aItN;
5028   SMDSAbs_ElementType aTypeE;
5029
5030   TNodeOfNodeListMap mapNewNodes;
5031
5032   // 1. Check data
5033   aNbE = theElements.size();
5034   // nothing to do
5035   if ( !aNbE )
5036     return EXTR_NO_ELEMENTS;
5037
5038   // 1.1 Track Pattern
5039   ASSERT( theTrack );
5040
5041   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5042
5043   aItE = pMeshDS->elementsIterator();
5044   while ( aItE->more() ) {
5045     const SMDS_MeshElement* pE = aItE->next();
5046     aTypeE = pE->GetType();
5047     // Pattern must contain links only
5048     if ( aTypeE != SMDSAbs_Edge )
5049       return EXTR_PATH_NOT_EDGE;
5050   }
5051
5052   list<SMESH_MeshEditor_PathPoint> fullList;
5053
5054   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5055
5056   if( aS == SMESH_Mesh::PseudoShape() ) {
5057     //Mesh without shape
5058     const SMDS_MeshNode* currentNode = NULL;
5059     const SMDS_MeshNode* prevNode = theN1;
5060     std::vector<const SMDS_MeshNode*> aNodesList;
5061     aNodesList.push_back(theN1);
5062     int nbEdges = 0, conn=0;
5063     const SMDS_MeshElement* prevElem = NULL;
5064     const SMDS_MeshElement* currentElem = NULL;
5065     int totalNbEdges = theTrack->NbEdges();
5066     SMDS_ElemIteratorPtr nIt;
5067
5068     //check start node
5069     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5070       return EXTR_BAD_STARTING_NODE;
5071     }
5072
5073     conn = nbEdgeConnectivity(theN1);
5074     if(conn > 2)
5075       return EXTR_PATH_NOT_EDGE;
5076
5077     aItE = theN1->GetInverseElementIterator();
5078     prevElem = aItE->next();
5079     currentElem = prevElem;
5080     //Get all nodes
5081     if(totalNbEdges == 1 ) {
5082       nIt = currentElem->nodesIterator();
5083       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5084       if(currentNode == prevNode)
5085         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5086       aNodesList.push_back(currentNode);
5087     } else {
5088       nIt = currentElem->nodesIterator();
5089       while( nIt->more() ) {
5090         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5091         if(currentNode == prevNode)
5092           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5093         aNodesList.push_back(currentNode);
5094
5095         //case of the closed mesh
5096         if(currentNode == theN1) {
5097           nbEdges++;
5098           break;
5099         }
5100
5101         conn = nbEdgeConnectivity(currentNode);
5102         if(conn > 2) {
5103           return EXTR_PATH_NOT_EDGE;
5104         }else if( conn == 1 && nbEdges > 0 ) {
5105           //End of the path
5106           nbEdges++;
5107           break;
5108         }else {
5109           prevNode = currentNode;
5110           aItE = currentNode->GetInverseElementIterator();
5111           currentElem = aItE->next();
5112           if( currentElem  == prevElem)
5113             currentElem = aItE->next();
5114           nIt = currentElem->nodesIterator();
5115           prevElem = currentElem;
5116           nbEdges++;
5117         }
5118       }
5119     }
5120
5121     if(nbEdges != totalNbEdges)
5122       return EXTR_PATH_NOT_EDGE;
5123
5124     TopTools_SequenceOfShape Edges;
5125     double x1,x2,y1,y2,z1,z2;
5126     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5127     int startNid = theN1->GetID();
5128     for(int i = 1; i < aNodesList.size(); i++) {
5129       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5130       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5131       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5132       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5133       list<SMESH_MeshEditor_PathPoint> LPP;
5134       aPrms.clear();
5135       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5136       LLPPs.push_back(LPP);
5137       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5138       else startNid = aNodesList[i-1]->GetID();
5139
5140     }
5141
5142     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5143     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5144     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5145     for(; itPP!=firstList.end(); itPP++) {
5146       fullList.push_back( *itPP );
5147     }
5148
5149     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5150     SMESH_MeshEditor_PathPoint PP2;
5151     fullList.pop_back();
5152     itLLPP++;
5153     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5154       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5155       itPP = currList.begin();
5156       PP2 = currList.front();
5157       gp_Dir D1 = PP1.Tangent();
5158       gp_Dir D2 = PP2.Tangent();
5159       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5160                            (D1.Z()+D2.Z())/2 ) );
5161       PP1.SetTangent(Dnew);
5162       fullList.push_back(PP1);
5163       itPP++;
5164       for(; itPP!=currList.end(); itPP++) {
5165         fullList.push_back( *itPP );
5166       }
5167       PP1 = fullList.back();
5168       fullList.pop_back();
5169     }
5170     fullList.push_back(PP1);
5171
5172   } // Sub-shape for the Pattern must be an Edge or Wire
5173   else if( aS.ShapeType() == TopAbs_EDGE ) {
5174     aTrackEdge = TopoDS::Edge( aS );
5175     // the Edge must not be degenerated
5176     if ( BRep_Tool::Degenerated( aTrackEdge ) )
5177       return EXTR_BAD_PATH_SHAPE;
5178     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5179     const SMDS_MeshNode* aN1 = 0;
5180     const SMDS_MeshNode* aN2 = 0;
5181     if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5182       aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5183       aN1 = aItN->next();
5184     }
5185     if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5186       aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5187       aN2 = aItN->next();
5188     }
5189     // starting node must be aN1 or aN2
5190     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5191       return EXTR_BAD_STARTING_NODE;
5192     aItN = pMeshDS->nodesIterator();
5193     while ( aItN->more() ) {
5194       const SMDS_MeshNode* pNode = aItN->next();
5195       if( pNode==aN1 || pNode==aN2 ) continue;
5196       const SMDS_EdgePosition* pEPos =
5197         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5198       double aT = pEPos->GetUParameter();
5199       aPrms.push_back( aT );
5200     }
5201     //Extrusion_Error err =
5202     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5203   }
5204   else if( aS.ShapeType() == TopAbs_WIRE ) {
5205     list< SMESH_subMesh* > LSM;
5206     TopTools_SequenceOfShape Edges;
5207     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5208     for(; eExp.More(); eExp.Next()) {
5209       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5210       if( BRep_Tool::Degenerated(E) ) continue;
5211       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5212       if(SM) {
5213         LSM.push_back(SM);
5214         Edges.Append(E);
5215       }
5216     }
5217     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5218     TopoDS_Vertex aVprev;
5219     TColStd_MapOfInteger UsedNums;
5220     int NbEdges = Edges.Length();
5221     int i = 1;
5222     for(; i<=NbEdges; i++) {
5223       int k = 0;
5224       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5225       for(; itLSM!=LSM.end(); itLSM++) {
5226         k++;
5227         if(UsedNums.Contains(k)) continue;
5228         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5229         SMESH_subMesh* locTrack = *itLSM;
5230         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5231         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5232         bool aN1isOK = false, aN2isOK = false;
5233         if ( aVprev.IsNull() ) {
5234           // if previous vertex is not yet defined, it means that we in the beginning of wire
5235           // and we have to find initial vertex corresponding to starting node theN1
5236           const SMDS_MeshNode* aN1 = 0;
5237           const SMDS_MeshNode* aN2 = 0;
5238
5239           if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5240             aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5241             aN1 = aItN->next();
5242           }
5243           if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5244             aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5245             aN2 = aItN->next();
5246           }
5247           // starting node must be aN1 or aN2
5248           aN1isOK = ( aN1 && aN1 == theN1 );
5249           aN2isOK = ( aN2 && aN2 == theN1 );
5250         }
5251         else {
5252           // we have specified ending vertex of the previous edge on the previous iteration
5253           // and we have just to check that it corresponds to any vertex in current segment
5254           aN1isOK = aVprev.IsSame( aV1 );
5255           aN2isOK = aVprev.IsSame( aV2 );
5256         }
5257         if ( !aN1isOK && !aN2isOK ) continue;
5258         // 2. Collect parameters on the track edge
5259         aPrms.clear();
5260         aItN = locMeshDS->GetNodes();
5261         while ( aItN->more() ) {
5262           const SMDS_MeshNode*     pNode = aItN->next();
5263           const SMDS_EdgePosition* pEPos =
5264             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5265           double aT = pEPos->GetUParameter();
5266           aPrms.push_back( aT );
5267         }
5268         list<SMESH_MeshEditor_PathPoint> LPP;
5269         //Extrusion_Error err =
5270         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5271         LLPPs.push_back(LPP);
5272         UsedNums.Add(k);
5273         // update startN for search following egde
5274         if ( aN1isOK ) aVprev = aV2;
5275         else           aVprev = aV1;
5276         break;
5277       }
5278     }
5279     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5280     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5281     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5282     for(; itPP!=firstList.end(); itPP++) {
5283       fullList.push_back( *itPP );
5284     }
5285     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5286     fullList.pop_back();
5287     itLLPP++;
5288     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5289       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5290       itPP = currList.begin();
5291       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5292       gp_Dir D1 = PP1.Tangent();
5293       gp_Dir D2 = PP2.Tangent();
5294       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5295       PP1.SetTangent(Dnew);
5296       fullList.push_back(PP1);
5297       itPP++;
5298       for(; itPP!=currList.end(); itPP++) {
5299         fullList.push_back( *itPP );
5300       }
5301       PP1 = fullList.back();
5302       fullList.pop_back();
5303     }
5304     // if wire not closed
5305     fullList.push_back(PP1);
5306     // else ???
5307   }
5308   else {
5309     return EXTR_BAD_PATH_SHAPE;
5310   }
5311
5312   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5313                           theHasRefPoint, theRefPoint, theMakeGroups);
5314 }
5315
5316
5317 //=======================================================================
5318 //function : MakeEdgePathPoints
5319 //purpose  : auxilary for ExtrusionAlongTrack
5320 //=======================================================================
5321 SMESH_MeshEditor::Extrusion_Error
5322 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5323                                      const TopoDS_Edge& aTrackEdge,
5324                                      bool FirstIsStart,
5325                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5326 {
5327   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5328   aTolVec=1.e-7;
5329   aTolVec2=aTolVec*aTolVec;
5330   double aT1, aT2;
5331   TopoDS_Vertex aV1, aV2;
5332   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5333   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5334   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5335   // 2. Collect parameters on the track edge
5336   aPrms.push_front( aT1 );
5337   aPrms.push_back( aT2 );
5338   // sort parameters
5339   aPrms.sort();
5340   if( FirstIsStart ) {
5341     if ( aT1 > aT2 ) {
5342       aPrms.reverse();
5343     }
5344   }
5345   else {
5346     if ( aT2 > aT1 ) {
5347       aPrms.reverse();
5348     }
5349   }
5350   // 3. Path Points
5351   SMESH_MeshEditor_PathPoint aPP;
5352   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5353   std::list<double>::iterator aItD = aPrms.begin();
5354   for(; aItD != aPrms.end(); ++aItD) {
5355     double aT = *aItD;
5356     gp_Pnt aP3D;
5357     gp_Vec aVec;
5358     aC3D->D1( aT, aP3D, aVec );
5359     aL2 = aVec.SquareMagnitude();
5360     if ( aL2 < aTolVec2 )
5361       return EXTR_CANT_GET_TANGENT;
5362     gp_Dir aTgt( aVec );
5363     aPP.SetPnt( aP3D );
5364     aPP.SetTangent( aTgt );
5365     aPP.SetParameter( aT );
5366     LPP.push_back(aPP);
5367   }
5368   return EXTR_OK;
5369 }
5370
5371
5372 //=======================================================================
5373 //function : MakeExtrElements
5374 //purpose  : auxilary for ExtrusionAlongTrack
5375 //=======================================================================
5376 SMESH_MeshEditor::Extrusion_Error
5377 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&  theElements,
5378                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5379                                    const bool theHasAngles,
5380                                    list<double>& theAngles,
5381                                    const bool theLinearVariation,
5382                                    const bool theHasRefPoint,
5383                                    const gp_Pnt& theRefPoint,
5384                                    const bool theMakeGroups)
5385 {
5386   MESSAGE("MakeExtrElements");
5387   //cout<<"MakeExtrElements  fullList.size() = "<<fullList.size()<<endl;
5388   int aNbTP = fullList.size();
5389   vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5390   // Angles
5391   if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5392     LinearAngleVariation(aNbTP-1, theAngles);
5393   }
5394   vector<double> aAngles( aNbTP );
5395   int j = 0;
5396   for(; j<aNbTP; ++j) {
5397     aAngles[j] = 0.;
5398   }
5399   if ( theHasAngles ) {
5400     double anAngle;;
5401     std::list<double>::iterator aItD = theAngles.begin();
5402     for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5403       anAngle = *aItD;
5404       aAngles[j] = anAngle;
5405     }
5406   }
5407   // fill vector of path points with angles
5408   //aPPs.resize(fullList.size());
5409   j = -1;
5410   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5411   for(; itPP!=fullList.end(); itPP++) {
5412     j++;
5413     SMESH_MeshEditor_PathPoint PP = *itPP;
5414     PP.SetAngle(aAngles[j]);
5415     aPPs[j] = PP;
5416   }
5417
5418   TNodeOfNodeListMap mapNewNodes;
5419   TElemOfVecOfNnlmiMap mapElemNewNodes;
5420   TElemOfElemListMap newElemsMap;
5421   TIDSortedElemSet::iterator itElem;
5422   double aX, aY, aZ;
5423   int aNb;
5424   SMDSAbs_ElementType aTypeE;
5425   // source elements for each generated one
5426   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5427
5428   // 3. Center of rotation aV0
5429   gp_Pnt aV0 = theRefPoint;
5430   gp_XYZ aGC;
5431   if ( !theHasRefPoint ) {
5432     aNb = 0;
5433     aGC.SetCoord( 0.,0.,0. );
5434
5435     itElem = theElements.begin();
5436     for ( ; itElem != theElements.end(); itElem++ ) {
5437       const SMDS_MeshElement* elem = *itElem;
5438
5439       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5440       while ( itN->more() ) {
5441         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5442         aX = node->X();
5443         aY = node->Y();
5444         aZ = node->Z();
5445
5446         if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5447           list<const SMDS_MeshNode*> aLNx;
5448           mapNewNodes[node] = aLNx;
5449           //
5450           gp_XYZ aXYZ( aX, aY, aZ );
5451           aGC += aXYZ;
5452           ++aNb;
5453         }
5454       }
5455     }
5456     aGC /= aNb;
5457     aV0.SetXYZ( aGC );
5458   } // if (!theHasRefPoint) {
5459   mapNewNodes.clear();
5460
5461   // 4. Processing the elements
5462   SMESHDS_Mesh* aMesh = GetMeshDS();
5463
5464   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5465     // check element type
5466     const SMDS_MeshElement* elem = *itElem;
5467     aTypeE = elem->GetType();
5468     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5469       continue;
5470
5471     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5472     newNodesItVec.reserve( elem->NbNodes() );
5473
5474     // loop on elem nodes
5475     int nodeIndex = -1;
5476     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5477     while ( itN->more() )
5478     {
5479       ++nodeIndex;
5480       // check if a node has been already processed
5481       const SMDS_MeshNode* node =
5482         static_cast<const SMDS_MeshNode*>( itN->next() );
5483       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5484       if ( nIt == mapNewNodes.end() ) {
5485         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5486         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5487
5488         // make new nodes
5489         aX = node->X();  aY = node->Y(); aZ = node->Z();
5490
5491         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5492         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5493         gp_Ax1 anAx1, anAxT1T0;
5494         gp_Dir aDT1x, aDT0x, aDT1T0;
5495
5496         aTolAng=1.e-4;
5497
5498         aV0x = aV0;
5499         aPN0.SetCoord(aX, aY, aZ);
5500
5501         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5502         aP0x = aPP0.Pnt();
5503         aDT0x= aPP0.Tangent();
5504         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5505
5506         for ( j = 1; j < aNbTP; ++j ) {
5507           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5508           aP1x = aPP1.Pnt();
5509           aDT1x = aPP1.Tangent();
5510           aAngle1x = aPP1.Angle();
5511
5512           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5513           // Translation
5514           gp_Vec aV01x( aP0x, aP1x );
5515           aTrsf.SetTranslation( aV01x );
5516
5517           // traslated point
5518           aV1x = aV0x.Transformed( aTrsf );
5519           aPN1 = aPN0.Transformed( aTrsf );
5520
5521           // rotation 1 [ T1,T0 ]
5522           aAngleT1T0=-aDT1x.Angle( aDT0x );
5523           if (fabs(aAngleT1T0) > aTolAng) {
5524             aDT1T0=aDT1x^aDT0x;
5525             anAxT1T0.SetLocation( aV1x );
5526             anAxT1T0.SetDirection( aDT1T0 );
5527             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5528
5529             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5530           }
5531
5532           // rotation 2
5533           if ( theHasAngles ) {
5534             anAx1.SetLocation( aV1x );
5535             anAx1.SetDirection( aDT1x );
5536             aTrsfRot.SetRotation( anAx1, aAngle1x );
5537
5538             aPN1 = aPN1.Transformed( aTrsfRot );
5539           }
5540
5541           // make new node
5542           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5543           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5544             // create additional node
5545             double x = ( aPN1.X() + aPN0.X() )/2.;
5546             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5547             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5548             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5549             myLastCreatedNodes.Append(newNode);
5550             srcNodes.Append( node );
5551             listNewNodes.push_back( newNode );
5552           }
5553           aX = aPN1.X();
5554           aY = aPN1.Y();
5555           aZ = aPN1.Z();
5556           const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5557           myLastCreatedNodes.Append(newNode);
5558           srcNodes.Append( node );
5559           listNewNodes.push_back( newNode );
5560
5561           aPN0 = aPN1;
5562           aP0x = aP1x;
5563           aV0x = aV1x;
5564           aDT0x = aDT1x;
5565         }
5566       }
5567
5568       else {
5569         // if current elem is quadratic and current node is not medium
5570         // we have to check - may be it is needed to insert additional nodes
5571         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5572           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5573           if(listNewNodes.size()==aNbTP-1) {
5574             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5575             gp_XYZ P(node->X(), node->Y(), node->Z());
5576             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5577             int i;
5578             for(i=0; i<aNbTP-1; i++) {
5579               const SMDS_MeshNode* N = *it;
5580               double x = ( N->X() + P.X() )/2.;
5581               double y = ( N->Y() + P.Y() )/2.;
5582               double z = ( N->Z() + P.Z() )/2.;
5583               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5584               srcNodes.Append( node );
5585               myLastCreatedNodes.Append(newN);
5586               aNodes[2*i] = newN;
5587               aNodes[2*i+1] = N;
5588               P = gp_XYZ(N->X(),N->Y(),N->Z());
5589             }
5590             listNewNodes.clear();
5591             for(i=0; i<2*(aNbTP-1); i++) {
5592               listNewNodes.push_back(aNodes[i]);
5593             }
5594           }
5595         }
5596       }
5597
5598       newNodesItVec.push_back( nIt );
5599     }
5600     // make new elements
5601     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5602     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
5603     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5604   }
5605
5606   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5607
5608   if ( theMakeGroups )
5609     generateGroups( srcNodes, srcElems, "extruded");
5610
5611   return EXTR_OK;
5612 }
5613
5614
5615 //=======================================================================
5616 //function : LinearAngleVariation
5617 //purpose  : auxilary for ExtrusionAlongTrack
5618 //=======================================================================
5619 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5620                                             list<double>& Angles)
5621 {
5622   int nbAngles = Angles.size();
5623   if( nbSteps > nbAngles ) {
5624     vector<double> theAngles(nbAngles);
5625     list<double>::iterator it = Angles.begin();
5626     int i = -1;
5627     for(; it!=Angles.end(); it++) {
5628       i++;
5629       theAngles[i] = (*it);
5630     }
5631     list<double> res;
5632     double rAn2St = double( nbAngles ) / double( nbSteps );
5633     double angPrev = 0, angle;
5634     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5635       double angCur = rAn2St * ( iSt+1 );
5636       double angCurFloor  = floor( angCur );
5637       double angPrevFloor = floor( angPrev );
5638       if ( angPrevFloor == angCurFloor )
5639         angle = rAn2St * theAngles[ int( angCurFloor ) ];
5640       else {
5641         int iP = int( angPrevFloor );
5642         double angPrevCeil = ceil(angPrev);
5643         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5644
5645         int iC = int( angCurFloor );
5646         if ( iC < nbAngles )
5647           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5648
5649         iP = int( angPrevCeil );
5650         while ( iC-- > iP )
5651           angle += theAngles[ iC ];
5652       }
5653       res.push_back(angle);
5654       angPrev = angCur;
5655     }
5656     Angles.clear();
5657     it = res.begin();
5658     for(; it!=res.end(); it++)
5659       Angles.push_back( *it );
5660   }
5661 }
5662
5663
5664 //================================================================================
5665 /*!
5666  * \brief Move or copy theElements applying theTrsf to their nodes
5667  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5668  *  \param theTrsf - transformation to apply
5669  *  \param theCopy - if true, create translated copies of theElems
5670  *  \param theMakeGroups - if true and theCopy, create translated groups
5671  *  \param theTargetMesh - mesh to copy translated elements into
5672  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5673  */
5674 //================================================================================
5675
5676 SMESH_MeshEditor::PGroupIDs
5677 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5678                              const gp_Trsf&     theTrsf,
5679                              const bool         theCopy,
5680                              const bool         theMakeGroups,
5681                              SMESH_Mesh*        theTargetMesh)
5682 {
5683   myLastCreatedElems.Clear();
5684   myLastCreatedNodes.Clear();
5685
5686   bool needReverse = false;
5687   string groupPostfix;
5688   switch ( theTrsf.Form() ) {
5689   case gp_PntMirror:
5690     MESSAGE("gp_PntMirror");
5691     needReverse = true;
5692     groupPostfix = "mirrored";
5693     break;
5694   case gp_Ax1Mirror:
5695     MESSAGE("gp_Ax1Mirror");
5696     groupPostfix = "mirrored";
5697     break;
5698   case gp_Ax2Mirror:
5699     MESSAGE("gp_Ax2Mirror");
5700     needReverse = true;
5701     groupPostfix = "mirrored";
5702     break;
5703   case gp_Rotation:
5704     MESSAGE("gp_Rotation");
5705     groupPostfix = "rotated";
5706     break;
5707   case gp_Translation:
5708     MESSAGE("gp_Translation");
5709     groupPostfix = "translated";
5710     break;
5711   case gp_Scale:
5712     MESSAGE("gp_Scale");
5713     groupPostfix = "scaled";
5714     break;
5715   case gp_CompoundTrsf: // different scale by axis
5716     MESSAGE("gp_CompoundTrsf");
5717     groupPostfix = "scaled";
5718     break;
5719   default:
5720     MESSAGE("default");
5721     needReverse = false;
5722     groupPostfix = "transformed";
5723   }
5724
5725   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5726   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5727   SMESHDS_Mesh* aMesh    = GetMeshDS();
5728
5729
5730   // map old node to new one
5731   TNodeNodeMap nodeMap;
5732
5733   // elements sharing moved nodes; those of them which have all
5734   // nodes mirrored but are not in theElems are to be reversed
5735   TIDSortedElemSet inverseElemSet;
5736
5737   // source elements for each generated one
5738   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5739
5740   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5741   TIDSortedElemSet orphanNode;
5742
5743   if ( theElems.empty() ) // transform the whole mesh
5744   {
5745     // add all elements
5746     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5747     while ( eIt->more() ) theElems.insert( eIt->next() );
5748     // add orphan nodes
5749     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5750     while ( nIt->more() )
5751     {
5752       const SMDS_MeshNode* node = nIt->next();
5753       if ( node->NbInverseElements() == 0)
5754         orphanNode.insert( node );
5755     }
5756   }
5757
5758   // loop on elements to transform nodes : first orphan nodes then elems
5759   TIDSortedElemSet::iterator itElem;
5760   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5761   for (int i=0; i<2; i++)
5762   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5763     const SMDS_MeshElement* elem = *itElem;
5764     if ( !elem )
5765       continue;
5766
5767     // loop on elem nodes
5768     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5769     while ( itN->more() ) {
5770
5771       const SMDS_MeshNode* node = cast2Node( itN->next() );
5772       // check if a node has been already transformed
5773       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5774         nodeMap.insert( make_pair ( node, node ));
5775       if ( !n2n_isnew.second )
5776         continue;
5777
5778       double coord[3];
5779       coord[0] = node->X();
5780       coord[1] = node->Y();
5781       coord[2] = node->Z();
5782       theTrsf.Transforms( coord[0], coord[1], coord[2] );
5783       if ( theTargetMesh ) {
5784         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5785         n2n_isnew.first->second = newNode;
5786         myLastCreatedNodes.Append(newNode);
5787         srcNodes.Append( node );
5788       }
5789       else if ( theCopy ) {
5790         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5791         n2n_isnew.first->second = newNode;
5792         myLastCreatedNodes.Append(newNode);
5793         srcNodes.Append( node );
5794       }
5795       else {
5796         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5797         // node position on shape becomes invalid
5798         const_cast< SMDS_MeshNode* > ( node )->SetPosition
5799           ( SMDS_SpacePosition::originSpacePosition() );
5800       }
5801
5802       // keep inverse elements
5803       if ( !theCopy && !theTargetMesh && needReverse ) {
5804         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5805         while ( invElemIt->more() ) {
5806           const SMDS_MeshElement* iel = invElemIt->next();
5807           inverseElemSet.insert( iel );
5808         }
5809       }
5810     }
5811   }
5812
5813   // either create new elements or reverse mirrored ones
5814   if ( !theCopy && !needReverse && !theTargetMesh )
5815     return PGroupIDs();
5816
5817   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5818   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5819     theElems.insert( *invElemIt );
5820
5821   // Replicate or reverse elements
5822
5823   std::vector<int> iForw;
5824   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5825   {
5826     const SMDS_MeshElement* elem = *itElem;
5827     if ( !elem ) continue;
5828
5829     SMDSAbs_GeometryType geomType = elem->GetGeomType();
5830     int                  nbNodes  = elem->NbNodes();
5831     if ( geomType == SMDSGeom_NONE ) continue; // node
5832
5833     switch ( geomType ) {
5834
5835     case SMDSGeom_POLYGON:  // ---------------------- polygon
5836       {
5837         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5838         int iNode = 0;
5839         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5840         while (itN->more()) {
5841           const SMDS_MeshNode* node =
5842             static_cast<const SMDS_MeshNode*>(itN->next());
5843           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5844           if (nodeMapIt == nodeMap.end())
5845             break; // not all nodes transformed
5846           if (needReverse) {
5847             // reverse mirrored faces and volumes
5848             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5849           } else {
5850             poly_nodes[iNode] = (*nodeMapIt).second;
5851           }
5852           iNode++;
5853         }
5854         if ( iNode != nbNodes )
5855           continue; // not all nodes transformed
5856
5857         if ( theTargetMesh ) {
5858           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5859           srcElems.Append( elem );
5860         }
5861         else if ( theCopy ) {
5862           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5863           srcElems.Append( elem );
5864         }
5865         else {
5866           aMesh->ChangePolygonNodes(elem, poly_nodes);
5867         }
5868       }
5869       break;
5870
5871     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
5872       {
5873         const SMDS_VtkVolume* aPolyedre =
5874           dynamic_cast<const SMDS_VtkVolume*>( elem );
5875         if (!aPolyedre) {
5876           MESSAGE("Warning: bad volumic element");
5877           continue;
5878         }
5879
5880         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5881         vector<int> quantities; quantities.reserve( nbNodes );
5882
5883         bool allTransformed = true;
5884         int nbFaces = aPolyedre->NbFaces();
5885         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5886           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5887           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5888             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5889             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5890             if (nodeMapIt == nodeMap.end()) {
5891               allTransformed = false; // not all nodes transformed
5892             } else {
5893               poly_nodes.push_back((*nodeMapIt).second);
5894             }
5895             if ( needReverse && allTransformed )
5896               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5897           }
5898           quantities.push_back(nbFaceNodes);
5899         }
5900         if ( !allTransformed )
5901           continue; // not all nodes transformed
5902
5903         if ( theTargetMesh ) {
5904           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5905           srcElems.Append( elem );
5906         }
5907         else if ( theCopy ) {
5908           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5909           srcElems.Append( elem );
5910         }
5911         else {
5912           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5913         }
5914       }
5915       break;
5916
5917     case SMDSGeom_BALL: // -------------------- Ball
5918       {
5919         if ( !theCopy && !theTargetMesh ) continue;
5920
5921         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5922         if (nodeMapIt == nodeMap.end())
5923           continue; // not all nodes transformed
5924
5925         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5926         if ( theTargetMesh ) {
5927           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5928           srcElems.Append( elem );
5929         }
5930         else {
5931           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5932           srcElems.Append( elem );
5933         }
5934       }
5935       break;
5936
5937     default: // ----------------------- Regular elements
5938
5939       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5940       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5941       const std::vector<int>& i = needReverse ? iRev : iForw;
5942
5943       // find transformed nodes
5944       vector<const SMDS_MeshNode*> nodes(nbNodes);
5945       int iNode = 0;
5946       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5947       while ( itN->more() ) {
5948         const SMDS_MeshNode* node =
5949           static_cast<const SMDS_MeshNode*>( itN->next() );
5950         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5951         if ( nodeMapIt == nodeMap.end() )
5952           break; // not all nodes transformed
5953         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5954       }
5955       if ( iNode != nbNodes )
5956         continue; // not all nodes transformed
5957
5958       if ( theTargetMesh ) {
5959         if ( SMDS_MeshElement* copy =
5960              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5961           myLastCreatedElems.Append( copy );
5962           srcElems.Append( elem );
5963         }
5964       }
5965       else if ( theCopy ) {
5966         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5967           srcElems.Append( elem );
5968       }
5969       else {
5970         // reverse element as it was reversed by transformation
5971         if ( nbNodes > 2 )
5972           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5973       }
5974     } // switch ( geomType )
5975
5976   } // loop on elements
5977
5978   PGroupIDs newGroupIDs;
5979
5980   if ( ( theMakeGroups && theCopy ) ||
5981        ( theMakeGroups && theTargetMesh ) )
5982     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5983
5984   return newGroupIDs;
5985 }
5986
5987 //=======================================================================
5988 /*!
5989  * \brief Create groups of elements made during transformation
5990  * \param nodeGens - nodes making corresponding myLastCreatedNodes
5991  * \param elemGens - elements making corresponding myLastCreatedElems
5992  * \param postfix - to append to names of new groups
5993  */
5994 //=======================================================================
5995
5996 SMESH_MeshEditor::PGroupIDs
5997 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5998                                  const SMESH_SequenceOfElemPtr& elemGens,
5999                                  const std::string&             postfix,
6000                                  SMESH_Mesh*                    targetMesh)
6001 {
6002   PGroupIDs newGroupIDs( new list<int> );
6003   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6004
6005   // Sort existing groups by types and collect their names
6006
6007   // to store an old group and a generated new ones
6008   using boost::tuple;
6009   using boost::make_tuple;
6010   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6011   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6012   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6013   // group names
6014   set< string > groupNames;
6015
6016   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6017   if ( !groupIt->more() ) return newGroupIDs;
6018
6019   int newGroupID = mesh->GetGroupIds().back()+1;
6020   while ( groupIt->more() )
6021   {
6022     SMESH_Group * group = groupIt->next();
6023     if ( !group ) continue;
6024     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6025     if ( !groupDS || groupDS->IsEmpty() ) continue;
6026     groupNames.insert    ( group->GetName() );
6027     groupDS->SetStoreName( group->GetName() );
6028     const SMDSAbs_ElementType type = groupDS->GetType();
6029     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6030     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6031     groupsByType[ groupDS->GetType() ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6032     orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
6033   }
6034
6035   // Loop on nodes and elements to add them in new groups
6036
6037   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6038   {
6039     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6040     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6041     if ( gens.Length() != elems.Length() )
6042       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6043
6044     // loop on created elements
6045     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6046     {
6047       const SMDS_MeshElement* sourceElem = gens( iElem );
6048       if ( !sourceElem ) {
6049         MESSAGE("generateGroups(): NULL source element");
6050         continue;
6051       }
6052       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6053       if ( groupsOldNew.empty() ) { // no groups of this type at all
6054         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6055           ++iElem; // skip all elements made by sourceElem
6056         continue;
6057       }
6058       // collect all elements made by the iElem-th sourceElem
6059       list< const SMDS_MeshElement* > resultElems;
6060       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6061         if ( resElem != sourceElem )
6062           resultElems.push_back( resElem );
6063       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6064         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6065           if ( resElem != sourceElem )
6066             resultElems.push_back( resElem );
6067
6068       // add resultElems to groups made by ones the sourceElem belongs to
6069       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6070       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6071       {
6072         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6073         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6074         {
6075           // fill in a new group
6076           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6077           list< const SMDS_MeshElement* > rejectedElems; // elements of other type
6078           list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6079           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6080             if ( !newGroup.Add( *resElemIt ))
6081               rejectedElems.push_back( *resElemIt );
6082
6083           // fill "top" group
6084           if ( !rejectedElems.empty() )
6085           {
6086             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6087             resLast = rejectedElems.end();
6088             for ( resElemIt = rejectedElems.begin(); resElemIt != resLast; ++resElemIt )
6089               !newTopGroup.Add( *resElemIt );
6090           }
6091         }
6092       }
6093     } // loop on created elements
6094   }// loop on nodes and elements
6095
6096   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6097
6098   list<int> topGrouIds;
6099   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6100   {
6101     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6102     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6103                                       orderedOldNewGroups[i]->get<2>() };
6104     const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6105     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6106     {
6107       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6108       if ( newGroupDS->IsEmpty() )
6109       {
6110         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6111       }
6112       else
6113       {
6114         // set group type
6115         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6116
6117         // make a name
6118         const bool isTop = ( nbNewGroups == 2 &&
6119                              newGroupDS->GetType() == oldGroupDS->GetType() );
6120         string name = oldGroupDS->GetStoreName();
6121         if ( !targetMesh ) {
6122           string suffix = ( isTop ? "top": postfix.c_str() );
6123           name += "_";
6124           name += suffix;
6125           int nb = 1;
6126           while ( !groupNames.insert( name ).second ) // name exists
6127             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6128         }
6129         else if ( isTop ) {
6130           name += "_top";
6131         }
6132         newGroupDS->SetStoreName( name.c_str() );
6133
6134         // make a SMESH_Groups
6135         mesh->AddGroup( newGroupDS );
6136         if ( isTop )
6137           topGrouIds.push_back( newGroupDS->GetID() );
6138         else
6139           newGroupIDs->push_back( newGroupDS->GetID() );
6140       }
6141     }
6142   }
6143   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6144
6145   return newGroupIDs;
6146 }
6147
6148 //================================================================================
6149 /*!
6150  * \brief Return list of group of nodes close to each other within theTolerance
6151  *        Search among theNodes or in the whole mesh if theNodes is empty using
6152  *        an Octree algorithm
6153  */
6154 //================================================================================
6155
6156 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6157                                             const double         theTolerance,
6158                                             TListOfListOfNodes & theGroupsOfNodes)
6159 {
6160   myLastCreatedElems.Clear();
6161   myLastCreatedNodes.Clear();
6162
6163   if ( theNodes.empty() )
6164   { // get all nodes in the mesh
6165     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6166     while ( nIt->more() )
6167       theNodes.insert( theNodes.end(),nIt->next());
6168   }
6169
6170   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6171 }
6172
6173
6174 //=======================================================================
6175 /*!
6176  * \brief Implementation of search for the node closest to point
6177  */
6178 //=======================================================================
6179
6180 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6181 {
6182   //---------------------------------------------------------------------
6183   /*!
6184    * \brief Constructor
6185    */
6186   SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6187   {
6188     myMesh = ( SMESHDS_Mesh* ) theMesh;
6189
6190     TIDSortedNodeSet nodes;
6191     if ( theMesh ) {
6192       SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6193       while ( nIt->more() )
6194         nodes.insert( nodes.end(), nIt->next() );
6195     }
6196     myOctreeNode = new SMESH_OctreeNode(nodes) ;
6197
6198     // get max size of a leaf box
6199     SMESH_OctreeNode* tree = myOctreeNode;
6200     while ( !tree->isLeaf() )
6201     {
6202       SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6203       if ( cIt->more() )
6204         tree = cIt->next();
6205     }
6206     myHalfLeafSize = tree->maxSize() / 2.;
6207   }
6208
6209   //---------------------------------------------------------------------
6210   /*!
6211    * \brief Move node and update myOctreeNode accordingly
6212    */
6213   void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6214   {
6215     myOctreeNode->UpdateByMoveNode( node, toPnt );
6216     myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6217   }
6218
6219   //---------------------------------------------------------------------
6220   /*!
6221    * \brief Do it's job
6222    */
6223   const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6224   {
6225     map<double, const SMDS_MeshNode*> dist2Nodes;
6226     myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6227     if ( !dist2Nodes.empty() )
6228       return dist2Nodes.begin()->second;
6229     list<const SMDS_MeshNode*> nodes;
6230     //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6231
6232     double minSqDist = DBL_MAX;
6233     if ( nodes.empty() )  // get all nodes of OctreeNode's closest to thePnt
6234     {
6235       // sort leafs by their distance from thePnt
6236       typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6237       TDistTreeMap treeMap;
6238       list< SMESH_OctreeNode* > treeList;
6239       list< SMESH_OctreeNode* >::iterator trIt;
6240       treeList.push_back( myOctreeNode );
6241
6242       gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6243       bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6244       for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6245       {
6246         SMESH_OctreeNode* tree = *trIt;
6247         if ( !tree->isLeaf() ) // put children to the queue
6248         {
6249           if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6250           SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6251           while ( cIt->more() )
6252             treeList.push_back( cIt->next() );
6253         }
6254         else if ( tree->NbNodes() ) // put a tree to the treeMap
6255         {
6256           const Bnd_B3d& box = *tree->getBox();
6257           double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6258           pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6259           if ( !it_in.second ) // not unique distance to box center
6260             treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6261         }
6262       }
6263       // find distance after which there is no sense to check tree's
6264       double sqLimit = DBL_MAX;
6265       TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6266       if ( treeMap.size() > 5 ) {
6267         SMESH_OctreeNode* closestTree = sqDist_tree->second;
6268         const Bnd_B3d& box = *closestTree->getBox();
6269         double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6270         sqLimit = limit * limit;
6271       }
6272       // get all nodes from trees
6273       for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6274         if ( sqDist_tree->first > sqLimit )
6275           break;
6276         SMESH_OctreeNode* tree = sqDist_tree->second;
6277         tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6278       }
6279     }
6280     // find closest among nodes
6281     minSqDist = DBL_MAX;
6282     const SMDS_MeshNode* closestNode = 0;
6283     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6284     for ( ; nIt != nodes.end(); ++nIt ) {
6285       double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6286       if ( minSqDist > sqDist ) {
6287         closestNode = *nIt;
6288         minSqDist = sqDist;
6289       }
6290     }
6291     return closestNode;
6292   }
6293
6294   //---------------------------------------------------------------------
6295   /*!
6296    * \brief Destructor
6297    */
6298   ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6299
6300   //---------------------------------------------------------------------
6301   /*!
6302    * \brief Return the node tree
6303    */
6304   const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6305
6306 private:
6307   SMESH_OctreeNode* myOctreeNode;
6308   SMESHDS_Mesh*     myMesh;
6309   double            myHalfLeafSize; // max size of a leaf box
6310 };
6311
6312 //=======================================================================
6313 /*!
6314  * \brief Return SMESH_NodeSearcher
6315  */
6316 //=======================================================================
6317
6318 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6319 {
6320   return new SMESH_NodeSearcherImpl( GetMeshDS() );
6321 }
6322
6323 // ========================================================================
6324 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6325 {
6326   const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6327   const int MaxLevel         = 7;  // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6328   const double NodeRadius = 1e-9;  // to enlarge bnd box of element
6329
6330   //=======================================================================
6331   /*!
6332    * \brief Octal tree of bounding boxes of elements
6333    */
6334   //=======================================================================
6335
6336   class ElementBndBoxTree : public SMESH_Octree
6337   {
6338   public:
6339
6340     ElementBndBoxTree(const SMDS_Mesh&     mesh,
6341                       SMDSAbs_ElementType  elemType,
6342                       SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6343                       double               tolerance = NodeRadius );
6344     void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6345     void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6346     void getElementsInSphere ( const gp_XYZ& center,
6347                                const double  radius, TIDSortedElemSet& foundElems);
6348     size_t getSize() { return std::max( _size, _elements.size() ); }
6349     ~ElementBndBoxTree();
6350
6351   protected:
6352     ElementBndBoxTree():_size(0) {}
6353     SMESH_Octree* newChild() const { return new ElementBndBoxTree; }
6354     void          buildChildrenData();
6355     Bnd_B3d*      buildRootBox();
6356   private:
6357     //!< Bounding box of element
6358     struct ElementBox : public Bnd_B3d
6359     {
6360       const SMDS_MeshElement* _element;
6361       int                     _refCount; // an ElementBox can be included in several tree branches
6362       ElementBox(const SMDS_MeshElement* elem, double tolerance);
6363     };
6364     vector< ElementBox* > _elements;
6365     size_t                _size;
6366   };
6367
6368   //================================================================================
6369   /*!
6370    * \brief ElementBndBoxTree creation
6371    */
6372   //================================================================================
6373
6374   ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6375     :SMESH_Octree( new SMESH_TreeLimit( MaxLevel, /*minSize=*/0. ))
6376   {
6377     int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6378     _elements.reserve( nbElems );
6379
6380     SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6381     while ( elemIt->more() )
6382       _elements.push_back( new ElementBox( elemIt->next(),tolerance  ));
6383
6384     compute();
6385   }
6386
6387   //================================================================================
6388   /*!
6389    * \brief Destructor
6390    */
6391   //================================================================================
6392
6393   ElementBndBoxTree::~ElementBndBoxTree()
6394   {
6395     for ( int i = 0; i < _elements.size(); ++i )
6396       if ( --_elements[i]->_refCount <= 0 )
6397         delete _elements[i];
6398   }
6399
6400   //================================================================================
6401   /*!
6402    * \brief Return the maximal box
6403    */
6404   //================================================================================
6405
6406   Bnd_B3d* ElementBndBoxTree::buildRootBox()
6407   {
6408     Bnd_B3d* box = new Bnd_B3d;
6409     for ( int i = 0; i < _elements.size(); ++i )
6410       box->Add( *_elements[i] );
6411     return box;
6412   }
6413
6414   //================================================================================
6415   /*!
6416    * \brief Redistrubute element boxes among children
6417    */
6418   //================================================================================
6419
6420   void ElementBndBoxTree::buildChildrenData()
6421   {
6422     for ( int i = 0; i < _elements.size(); ++i )
6423     {
6424       for (int j = 0; j < 8; j++)
6425       {
6426         if ( !_elements[i]->IsOut( *myChildren[j]->getBox() ))
6427         {
6428           _elements[i]->_refCount++;
6429           ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6430         }
6431       }
6432       _elements[i]->_refCount--;
6433     }
6434     _size = _elements.size();
6435     SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6436
6437     for (int j = 0; j < 8; j++)
6438     {
6439       ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6440       if ( child->_elements.size() <= MaxNbElemsInLeaf )
6441         child->myIsLeaf = true;
6442
6443       if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6444         SMESHUtils::CompactVector( child->_elements );
6445     }
6446   }
6447
6448   //================================================================================
6449   /*!
6450    * \brief Return elements which can include the point
6451    */
6452   //================================================================================
6453
6454   void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt&     point,
6455                                                 TIDSortedElemSet& foundElems)
6456   {
6457     if ( getBox()->IsOut( point.XYZ() ))
6458       return;
6459
6460     if ( isLeaf() )
6461     {
6462       for ( int i = 0; i < _elements.size(); ++i )
6463         if ( !_elements[i]->IsOut( point.XYZ() ))
6464           foundElems.insert( _elements[i]->_element );
6465     }
6466     else
6467     {
6468       for (int i = 0; i < 8; i++)
6469         ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6470     }
6471   }
6472
6473   //================================================================================
6474   /*!
6475    * \brief Return elements which can be intersected by the line
6476    */
6477   //================================================================================
6478
6479   void ElementBndBoxTree::getElementsNearLine( const gp_Ax1&     line,
6480                                                TIDSortedElemSet& foundElems)
6481   {
6482     if ( getBox()->IsOut( line ))
6483       return;
6484
6485     if ( isLeaf() )
6486     {
6487       for ( int i = 0; i < _elements.size(); ++i )
6488         if ( !_elements[i]->IsOut( line ))
6489           foundElems.insert( _elements[i]->_element );
6490     }
6491     else
6492     {
6493       for (int i = 0; i < 8; i++)
6494         ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6495     }
6496   }
6497
6498   //================================================================================
6499   /*!
6500    * \brief Return elements from leaves intersecting the sphere
6501    */
6502   //================================================================================
6503
6504   void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ&     center,
6505                                                 const double      radius,
6506                                                 TIDSortedElemSet& foundElems)
6507   {
6508     if ( getBox()->IsOut( center, radius ))
6509       return;
6510
6511     if ( isLeaf() )
6512     {
6513       for ( int i = 0; i < _elements.size(); ++i )
6514         if ( !_elements[i]->IsOut( center, radius ))
6515           foundElems.insert( _elements[i]->_element );
6516     }
6517     else
6518     {
6519       for (int i = 0; i < 8; i++)
6520         ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6521     }
6522   }
6523
6524   //================================================================================
6525   /*!
6526    * \brief Construct the element box
6527    */
6528   //================================================================================
6529
6530   ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6531   {
6532     _element  = elem;
6533     _refCount = 1;
6534     SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6535     while ( nIt->more() )
6536       Add( SMESH_TNodeXYZ( nIt->next() ));
6537     Enlarge( tolerance );
6538   }
6539
6540 } // namespace
6541
6542 //=======================================================================
6543 /*!
6544  * \brief Implementation of search for the elements by point and
6545  *        of classification of point in 2D mesh
6546  */
6547 //=======================================================================
6548
6549 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6550 {
6551   SMESHDS_Mesh*                _mesh;
6552   SMDS_ElemIteratorPtr         _meshPartIt;
6553   ElementBndBoxTree*           _ebbTree;
6554   SMESH_NodeSearcherImpl*      _nodeSearcher;
6555   SMDSAbs_ElementType          _elementType;
6556   double                       _tolerance;
6557   bool                         _outerFacesFound;
6558   set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6559
6560   SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6561     : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6562   ~SMESH_ElementSearcherImpl()
6563   {
6564     if ( _ebbTree )      delete _ebbTree;      _ebbTree      = 0;
6565     if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6566   }
6567   virtual int FindElementsByPoint(const gp_Pnt&                      point,
6568                                   SMDSAbs_ElementType                type,
6569                                   vector< const SMDS_MeshElement* >& foundElements);
6570   virtual TopAbs_State GetPointState(const gp_Pnt& point);
6571   virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt&       point,
6572                                                  SMDSAbs_ElementType type );
6573
6574   void GetElementsNearLine( const gp_Ax1&                      line,
6575                             SMDSAbs_ElementType                type,
6576                             vector< const SMDS_MeshElement* >& foundElems);
6577   double getTolerance();
6578   bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6579                             const double tolerance, double & param);
6580   void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6581   bool isOuterBoundary(const SMDS_MeshElement* face) const
6582   {
6583     return _outerFaces.empty() || _outerFaces.count(face);
6584   }
6585   struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6586   {
6587     const SMDS_MeshElement* _face;
6588     gp_Vec                  _faceNorm;
6589     bool                    _coincides; //!< the line lays in face plane
6590     TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6591       : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6592   };
6593   struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6594   {
6595     SMESH_TLink      _link;
6596     TIDSortedElemSet _faces;
6597     TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6598       : _link( n1, n2 ), _faces( &face, &face + 1) {}
6599   };
6600 };
6601
6602 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6603 {
6604   return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6605              << ", _coincides="<<i._coincides << ")";
6606 }
6607
6608 //=======================================================================
6609 /*!
6610  * \brief define tolerance for search
6611  */
6612 //=======================================================================
6613
6614 double SMESH_ElementSearcherImpl::getTolerance()
6615 {
6616   if ( _tolerance < 0 )
6617   {
6618     const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6619
6620     _tolerance = 0;
6621     if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6622     {
6623       double boxSize = _nodeSearcher->getTree()->maxSize();
6624       _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6625     }
6626     else if ( _ebbTree && meshInfo.NbElements() > 0 )
6627     {
6628       double boxSize = _ebbTree->maxSize();
6629       _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6630     }
6631     if ( _tolerance == 0 )
6632     {
6633       // define tolerance by size of a most complex element
6634       int complexType = SMDSAbs_Volume;
6635       while ( complexType > SMDSAbs_All &&
6636               meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6637         --complexType;
6638       if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6639       double elemSize;
6640       if ( complexType == int( SMDSAbs_Node ))
6641       {
6642         SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6643         elemSize = 1;
6644         if ( meshInfo.NbNodes() > 2 )
6645           elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6646       }
6647       else
6648       {
6649         SMDS_ElemIteratorPtr elemIt =
6650             _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6651         const SMDS_MeshElement* elem = elemIt->next();
6652         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6653         SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6654         elemSize = 0;
6655         while ( nodeIt->more() )
6656         {
6657           double dist = n1.Distance( cast2Node( nodeIt->next() ));
6658           elemSize = max( dist, elemSize );
6659         }
6660       }
6661       _tolerance = 1e-4 * elemSize;
6662     }
6663   }
6664   return _tolerance;
6665 }
6666
6667 //================================================================================
6668 /*!
6669  * \brief Find intersection of the line and an edge of face and return parameter on line
6670  */
6671 //================================================================================
6672
6673 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin&           line,
6674                                                      const SMDS_MeshElement* face,
6675                                                      const double            tol,
6676                                                      double &                param)
6677 {
6678   int nbInts = 0;
6679   param = 0;
6680
6681   GeomAPI_ExtremaCurveCurve anExtCC;
6682   Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6683
6684   int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6685   for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6686   {
6687     GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6688                          SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6689     anExtCC.Init( lineCurve, edge);
6690     if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6691     {
6692       Quantity_Parameter pl, pe;
6693       anExtCC.LowerDistanceParameters( pl, pe );
6694       param += pl;
6695       if ( ++nbInts == 2 )
6696         break;
6697     }
6698   }
6699   if ( nbInts > 0 ) param /= nbInts;
6700   return nbInts > 0;
6701 }
6702 //================================================================================
6703 /*!
6704  * \brief Find all faces belonging to the outer boundary of mesh
6705  */
6706 //================================================================================
6707
6708 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6709 {
6710   if ( _outerFacesFound ) return;
6711
6712   // Collect all outer faces by passing from one outer face to another via their links
6713   // and BTW find out if there are internal faces at all.
6714
6715   // checked links and links where outer boundary meets internal one
6716   set< SMESH_TLink > visitedLinks, seamLinks;
6717
6718   // links to treat with already visited faces sharing them
6719   list < TFaceLink > startLinks;
6720
6721   // load startLinks with the first outerFace
6722   startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6723   _outerFaces.insert( outerFace );
6724
6725   TIDSortedElemSet emptySet;
6726   while ( !startLinks.empty() )
6727   {
6728     const SMESH_TLink& link  = startLinks.front()._link;
6729     TIDSortedElemSet&  faces = startLinks.front()._faces;
6730
6731     outerFace = *faces.begin();
6732     // find other faces sharing the link
6733     const SMDS_MeshElement* f;
6734     while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6735       faces.insert( f );
6736
6737     // select another outer face among the found
6738     const SMDS_MeshElement* outerFace2 = 0;
6739     if ( faces.size() == 2 )
6740     {
6741       outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6742     }
6743     else if ( faces.size() > 2 )
6744     {
6745       seamLinks.insert( link );
6746
6747       // link direction within the outerFace
6748       gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6749                    SMESH_TNodeXYZ( link.node2()));
6750       int i1 = outerFace->GetNodeIndex( link.node1() );
6751       int i2 = outerFace->GetNodeIndex( link.node2() );
6752       bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6753       if ( rev ) n1n2.Reverse();
6754       // outerFace normal
6755       gp_XYZ ofNorm, fNorm;
6756       if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6757       {
6758         // direction from the link inside outerFace
6759         gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6760         // sort all other faces by angle with the dirInOF
6761         map< double, const SMDS_MeshElement* > angle2Face;
6762         set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6763         for ( ; face != faces.end(); ++face )
6764         {
6765           if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6766             continue;
6767           gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6768           double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6769           if ( angle < 0 ) angle += 2. * M_PI;
6770           angle2Face.insert( make_pair( angle, *face ));
6771         }
6772         if ( !angle2Face.empty() )
6773           outerFace2 = angle2Face.begin()->second;
6774       }
6775     }
6776     // store the found outer face and add its links to continue seaching from
6777     if ( outerFace2 )
6778     {
6779       _outerFaces.insert( outerFace );
6780       int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6781       for ( int i = 0; i < nbNodes; ++i )
6782       {
6783         SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6784         if ( visitedLinks.insert( link2 ).second )
6785           startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6786       }
6787     }
6788     startLinks.pop_front();
6789   }
6790   _outerFacesFound = true;
6791
6792   if ( !seamLinks.empty() )
6793   {
6794     // There are internal boundaries touching the outher one,
6795     // find all faces of internal boundaries in order to find
6796     // faces of boundaries of holes, if any.
6797
6798   }
6799   else
6800   {
6801     _outerFaces.clear();
6802   }
6803 }
6804
6805 //=======================================================================
6806 /*!
6807  * \brief Find elements of given type where the given point is IN or ON.
6808  *        Returns nb of found elements and elements them-selves.
6809  *
6810  * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6811  */
6812 //=======================================================================
6813
6814 int SMESH_ElementSearcherImpl::
6815 FindElementsByPoint(const gp_Pnt&                      point,
6816                     SMDSAbs_ElementType                type,
6817                     vector< const SMDS_MeshElement* >& foundElements)
6818 {
6819   foundElements.clear();
6820
6821   double tolerance = getTolerance();
6822
6823   // =================================================================================
6824   if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6825   {
6826     if ( !_nodeSearcher )
6827       _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6828
6829     const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6830     if ( !closeNode ) return foundElements.size();
6831
6832     if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6833       return foundElements.size(); // to far from any node
6834
6835     if ( type == SMDSAbs_Node )
6836     {
6837       foundElements.push_back( closeNode );
6838     }
6839     else
6840     {
6841       SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6842       while ( elemIt->more() )
6843         foundElements.push_back( elemIt->next() );
6844     }
6845   }
6846   // =================================================================================
6847   else // elements more complex than 0D
6848   {
6849     if ( !_ebbTree || _elementType != type )
6850     {
6851       if ( _ebbTree ) delete _ebbTree;
6852       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6853     }
6854     TIDSortedElemSet suspectElems;
6855     _ebbTree->getElementsNearPoint( point, suspectElems );
6856     TIDSortedElemSet::iterator elem = suspectElems.begin();
6857     for ( ; elem != suspectElems.end(); ++elem )
6858       if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6859         foundElements.push_back( *elem );
6860   }
6861   return foundElements.size();
6862 }
6863
6864 //=======================================================================
6865 /*!
6866  * \brief Find an element of given type most close to the given point
6867  *
6868  * WARNING: Only face search is implemeneted so far
6869  */
6870 //=======================================================================
6871
6872 const SMDS_MeshElement*
6873 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt&       point,
6874                                           SMDSAbs_ElementType type )
6875 {
6876   const SMDS_MeshElement* closestElem = 0;
6877
6878   if ( type == SMDSAbs_Face )
6879   {
6880     if ( !_ebbTree || _elementType != type )
6881     {
6882       if ( _ebbTree ) delete _ebbTree;
6883       _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6884     }
6885     TIDSortedElemSet suspectElems;
6886     _ebbTree->getElementsNearPoint( point, suspectElems );
6887
6888     if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6889     {
6890       gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox()->CornerMin() +
6891                                  _ebbTree->getBox()->CornerMax() );
6892       double radius;
6893       if ( _ebbTree->getBox()->IsOut( point.XYZ() ))
6894         radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6895       else
6896         radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6897       while ( suspectElems.empty() )
6898       {
6899         _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6900         radius *= 1.1;
6901       }
6902     }
6903     double minDist = std::numeric_limits<double>::max();
6904     multimap< double, const SMDS_MeshElement* > dist2face;
6905     TIDSortedElemSet::iterator elem = suspectElems.begin();
6906     for ( ; elem != suspectElems.end(); ++elem )
6907     {
6908       double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6909                                                    point );
6910       if ( dist < minDist + 1e-10)
6911       {
6912         minDist = dist;
6913         dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6914       }
6915     }
6916     if ( !dist2face.empty() )
6917     {
6918       multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6919       closestElem = d2f->second;
6920       // if there are several elements at the same distance, select one
6921       // with GC closest to the point
6922       typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6923       double minDistToGC = 0;
6924       for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6925       {
6926         if ( minDistToGC == 0 )
6927         {
6928           gp_XYZ gc(0,0,0);
6929           gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6930                            TXyzIterator(), gc ) / closestElem->NbNodes();
6931           minDistToGC = point.SquareDistance( gc );
6932         }
6933         gp_XYZ gc(0,0,0);
6934         gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6935                          TXyzIterator(), gc ) / d2f->second->NbNodes();
6936         double d = point.SquareDistance( gc );
6937         if ( d < minDistToGC )
6938         {
6939           minDistToGC = d;
6940           closestElem = d2f->second;
6941         }
6942       }
6943       // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6944       //      <<closestElem->GetID() << " DIST " << minDist << endl;
6945     }
6946   }
6947   else
6948   {
6949     // NOT IMPLEMENTED SO FAR
6950   }
6951   return closestElem;
6952 }
6953
6954
6955 //================================================================================
6956 /*!
6957  * \brief Classify the given point in the closed 2D mesh
6958  */
6959 //================================================================================
6960
6961 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6962 {
6963   double tolerance = getTolerance();
6964   if ( !_ebbTree || _elementType != SMDSAbs_Face )
6965   {
6966     if ( _ebbTree ) delete _ebbTree;
6967     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6968   }
6969   // Algo: analyse transition of a line starting at the point through mesh boundary;
6970   // try three lines parallel to axis of the coordinate system and perform rough
6971   // analysis. If solution is not clear perform thorough analysis.
6972
6973   const int nbAxes = 3;
6974   gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6975   map< double, TInters >   paramOnLine2TInters[ nbAxes ];
6976   list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6977   multimap< int, int > nbInt2Axis; // to find the simplest case
6978   for ( int axis = 0; axis < nbAxes; ++axis )
6979   {
6980     gp_Ax1 lineAxis( point, axisDir[axis]);
6981     gp_Lin line    ( lineAxis );
6982
6983     TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6984     _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6985
6986     // Intersect faces with the line
6987
6988     map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6989     TIDSortedElemSet::iterator face = suspectFaces.begin();
6990     for ( ; face != suspectFaces.end(); ++face )
6991     {
6992       // get face plane
6993       gp_XYZ fNorm;
6994       if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6995       gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6996
6997       // perform intersection
6998       IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6999       if ( !intersection.IsDone() )
7000         continue;
7001       if ( intersection.IsInQuadric() )
7002       {
7003         tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
7004       }
7005       else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
7006       {
7007         gp_Pnt intersectionPoint = intersection.Point(1);
7008         if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
7009           u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
7010       }
7011     }
7012     // Analyse intersections roughly
7013
7014     int nbInter = u2inters.size();
7015     if ( nbInter == 0 )
7016       return TopAbs_OUT;
7017
7018     double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
7019     if ( nbInter == 1 ) // not closed mesh
7020       return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7021
7022     if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7023       return TopAbs_ON;
7024
7025     if ( (f<0) == (l<0) )
7026       return TopAbs_OUT;
7027
7028     int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
7029     int nbIntAfterPoint  = nbInter - nbIntBeforePoint;
7030     if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7031       return TopAbs_IN;
7032
7033     nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
7034
7035     if ( _outerFacesFound ) break; // pass to thorough analysis
7036
7037   } // three attempts - loop on CS axes
7038
7039   // Analyse intersections thoroughly.
7040   // We make two loops maximum, on the first one we only exclude touching intersections,
7041   // on the second, if situation is still unclear, we gather and use information on
7042   // position of faces (internal or outer). If faces position is already gathered,
7043   // we make the second loop right away.
7044
7045   for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
7046   {
7047     multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
7048     for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
7049     {
7050       int axis = nb_axis->second;
7051       map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
7052
7053       gp_Ax1 lineAxis( point, axisDir[axis]);
7054       gp_Lin line    ( lineAxis );
7055
7056       // add tangent intersections to u2inters
7057       double param;
7058       list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
7059       for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
7060         if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
7061           u2inters.insert(make_pair( param, *tgtInt ));
7062       tangentInters[ axis ].clear();
7063
7064       // Count intersections before and after the point excluding touching ones.
7065       // If hasPositionInfo we count intersections of outer boundary only
7066
7067       int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
7068       double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
7069       map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
7070       bool ok = ! u_int1->second._coincides;
7071       while ( ok && u_int1 != u2inters.end() )
7072       {
7073         double u = u_int1->first;
7074         bool touchingInt = false;
7075         if ( ++u_int2 != u2inters.end() )
7076         {
7077           // skip intersections at the same point (if the line passes through edge or node)
7078           int nbSamePnt = 0;
7079           while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
7080           {
7081             ++nbSamePnt;
7082             ++u_int2;
7083           }
7084
7085           // skip tangent intersections
7086           int nbTgt = 0;
7087           const SMDS_MeshElement* prevFace = u_int1->second._face;
7088           while ( ok && u_int2->second._coincides )
7089           {
7090             if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
7091               ok = false;
7092             else
7093             {
7094               nbTgt++;
7095               u_int2++;
7096               ok = ( u_int2 != u2inters.end() );
7097             }
7098           }
7099           if ( !ok ) break;
7100
7101           // skip intersections at the same point after tangent intersections
7102           if ( nbTgt > 0 )
7103           {
7104             double u2 = u_int2->first;
7105             ++u_int2;
7106             while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
7107             {
7108               ++nbSamePnt;
7109               ++u_int2;
7110             }
7111           }
7112           // decide if we skipped a touching intersection
7113           if ( nbSamePnt + nbTgt > 0 )
7114           {
7115             double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
7116             map< double, TInters >::iterator u_int = u_int1;
7117             for ( ; u_int != u_int2; ++u_int )
7118             {
7119               if ( u_int->second._coincides ) continue;
7120               double dot = u_int->second._faceNorm * line.Direction();
7121               if ( dot > maxDot ) maxDot = dot;
7122               if ( dot < minDot ) minDot = dot;
7123             }
7124             touchingInt = ( minDot*maxDot < 0 );
7125           }
7126         }
7127         if ( !touchingInt )
7128         {
7129           if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
7130           {
7131             if ( u < 0 )
7132               ++nbIntBeforePoint;
7133             else
7134               ++nbIntAfterPoint;
7135           }
7136           if ( u < f ) f = u;
7137           if ( u > l ) l = u;
7138         }
7139
7140         u_int1 = u_int2; // to next intersection
7141
7142       } // loop on intersections with one line
7143
7144       if ( ok )
7145       {
7146         if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
7147           return TopAbs_ON;
7148
7149         if ( nbIntBeforePoint == 0  || nbIntAfterPoint == 0)
7150           return TopAbs_OUT;
7151
7152         if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7153           return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7154
7155         if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7156           return TopAbs_IN;
7157
7158         if ( (f<0) == (l<0) )
7159           return TopAbs_OUT;
7160
7161         if ( hasPositionInfo )
7162           return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7163       }
7164     } // loop on intersections of the tree lines - thorough analysis
7165
7166     if ( !hasPositionInfo )
7167     {
7168       // gather info on faces position - is face in the outer boundary or not
7169       map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7170       findOuterBoundary( u2inters.begin()->second._face );
7171     }
7172
7173   } // two attempts - with and w/o faces position info in the mesh
7174
7175   return TopAbs_UNKNOWN;
7176 }
7177
7178 //=======================================================================
7179 /*!
7180  * \brief Return elements possibly intersecting the line
7181  */
7182 //=======================================================================
7183
7184 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1&                      line,
7185                                                      SMDSAbs_ElementType                type,
7186                                                      vector< const SMDS_MeshElement* >& foundElems)
7187 {
7188   if ( !_ebbTree || _elementType != type )
7189   {
7190     if ( _ebbTree ) delete _ebbTree;
7191     _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7192   }
7193   TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7194   _ebbTree->getElementsNearLine( line, suspectFaces );
7195   foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7196 }
7197
7198 //=======================================================================
7199 /*!
7200  * \brief Return SMESH_ElementSearcher
7201  */
7202 //=======================================================================
7203
7204 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7205 {
7206   return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7207 }
7208
7209 //=======================================================================
7210 /*!
7211  * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7212  */
7213 //=======================================================================
7214
7215 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7216 {
7217   return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7218 }
7219
7220 //=======================================================================
7221 /*!
7222  * \brief Return true if the point is IN or ON of the element
7223  */
7224 //=======================================================================
7225
7226 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7227 {
7228   if ( element->GetType() == SMDSAbs_Volume)
7229   {
7230     return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7231   }
7232
7233   // get ordered nodes
7234
7235   vector< gp_XYZ > xyz;
7236   vector<const SMDS_MeshNode*> nodeList;
7237
7238   SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7239   if ( element->IsQuadratic() ) {
7240     if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7241       nodeIt = f->interlacedNodesElemIterator();
7242     else if (const SMDS_VtkEdge*  e =dynamic_cast<const SMDS_VtkEdge*>(element))
7243       nodeIt = e->interlacedNodesElemIterator();
7244   }
7245   while ( nodeIt->more() )
7246     {
7247       const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7248       xyz.push_back( SMESH_TNodeXYZ(node) );
7249       nodeList.push_back(node);
7250     }
7251
7252   int i, nbNodes = element->NbNodes();
7253
7254   if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7255   {
7256     // compute face normal
7257     gp_Vec faceNorm(0,0,0);
7258     xyz.push_back( xyz.front() );
7259     nodeList.push_back( nodeList.front() );
7260     for ( i = 0; i < nbNodes; ++i )
7261     {
7262       gp_Vec edge1( xyz[i+1], xyz[i]);
7263       gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7264       faceNorm += edge1 ^ edge2;
7265     }
7266     double normSize = faceNorm.Magnitude();
7267     if ( normSize <= tol )
7268     {
7269       // degenerated face: point is out if it is out of all face edges
7270       for ( i = 0; i < nbNodes; ++i )
7271       {
7272         SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7273         if ( !IsOut( &edge, point, tol ))
7274           return false;
7275       }
7276       return true;
7277     }
7278     faceNorm /= normSize;
7279
7280     // check if the point lays on face plane
7281     gp_Vec n2p( xyz[0], point );
7282     if ( fabs( n2p * faceNorm ) > tol )
7283       return true; // not on face plane
7284
7285     // check if point is out of face boundary:
7286     // define it by closest transition of a ray point->infinity through face boundary
7287     // on the face plane.
7288     // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7289     // to find intersections of the ray with the boundary.
7290     gp_Vec ray = n2p;
7291     gp_Vec plnNorm = ray ^ faceNorm;
7292     normSize = plnNorm.Magnitude();
7293     if ( normSize <= tol ) return false; // point coincides with the first node
7294     plnNorm /= normSize;
7295     // for each node of the face, compute its signed distance to the plane
7296     vector<double> dist( nbNodes + 1);
7297     for ( i = 0; i < nbNodes; ++i )
7298     {
7299       gp_Vec n2p( xyz[i], point );
7300       dist[i] = n2p * plnNorm;
7301     }
7302     dist.back() = dist.front();
7303     // find the closest intersection
7304     int    iClosest = -1;
7305     double rClosest, distClosest = 1e100;;
7306     gp_Pnt pClosest;
7307     for ( i = 0; i < nbNodes; ++i )
7308     {
7309       double r;
7310       if ( fabs( dist[i]) < tol )
7311         r = 0.;
7312       else if ( fabs( dist[i+1]) < tol )
7313         r = 1.;
7314       else if ( dist[i] * dist[i+1] < 0 )
7315         r = dist[i] / ( dist[i] - dist[i+1] );
7316       else
7317         continue; // no intersection
7318       gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7319       gp_Vec p2int ( point, pInt);
7320       if ( p2int * ray > -tol ) // right half-space
7321       {
7322         double intDist = p2int.SquareMagnitude();
7323         if ( intDist < distClosest )
7324         {
7325           iClosest = i;
7326           rClosest = r;
7327           pClosest = pInt;
7328           distClosest = intDist;
7329         }
7330       }
7331     }
7332     if ( iClosest < 0 )
7333       return true; // no intesections - out
7334
7335     // analyse transition
7336     gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7337     gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7338     gp_Vec p2int ( point, pClosest );
7339     bool out = (edgeNorm * p2int) < -tol;
7340     if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7341       return out;
7342
7343     // ray pass through a face node; analyze transition through an adjacent edge
7344     gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7345     gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7346     gp_Vec edgeAdjacent( p1, p2 );
7347     gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7348     bool out2 = (edgeNorm2 * p2int) < -tol;
7349
7350     bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7351     return covexCorner ? (out || out2) : (out && out2);
7352   }
7353   if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7354   {
7355     // point is out of edge if it is NOT ON any straight part of edge
7356     // (we consider quadratic edge as being composed of two straight parts)
7357     for ( i = 1; i < nbNodes; ++i )
7358     {
7359       gp_Vec edge( xyz[i-1], xyz[i]);
7360       gp_Vec n1p ( xyz[i-1], point);
7361       double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7362       if ( dist > tol )
7363         continue;
7364       gp_Vec n2p( xyz[i], point );
7365       if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7366         continue;
7367       return false; // point is ON this part
7368     }
7369     return true;
7370   }
7371   // Node or 0D element -------------------------------------------------------------------------
7372   {
7373     gp_Vec n2p ( xyz[0], point );
7374     return n2p.Magnitude() <= tol;
7375   }
7376   return true;
7377 }
7378
7379 //=======================================================================
7380
7381 namespace
7382 {
7383   // Position of a point relative to a segment
7384   //            .           .
7385   //            .  LEFT     .
7386   //            .           .
7387   //  VERTEX 1  o----ON----->  VERTEX 2
7388   //            .           .
7389   //            .  RIGHT    .
7390   //            .           .
7391   enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7392                       POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7393   struct PointPos
7394   {
7395     PositionName _name;
7396     int          _index; // index of vertex or segment
7397
7398     PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7399     bool operator < (const PointPos& other ) const
7400     {
7401       if ( _name == other._name )
7402         return  ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7403       return _name < other._name;
7404     }
7405   };
7406
7407   //================================================================================
7408   /*!
7409    * \brief Return of a point relative to a segment
7410    *  \param point2D      - the point to analyze position of
7411    *  \param xyVec        - end points of segments
7412    *  \param index0       - 0-based index of the first point of segment
7413    *  \param posToFindOut - flags of positions to detect
7414    *  \retval PointPos - point position
7415    */
7416   //================================================================================
7417
7418   PointPos getPointPosition( const gp_XY& point2D,
7419                              const gp_XY* segEnds,
7420                              const int    index0 = 0,
7421                              const int    posToFindOut = POS_ALL)
7422   {
7423     const gp_XY& p1 = segEnds[ index0   ];
7424     const gp_XY& p2 = segEnds[ index0+1 ];
7425     const gp_XY grad = p2 - p1;
7426
7427     if ( posToFindOut & POS_VERTEX )
7428     {
7429       // check if the point2D is at "vertex 1" zone
7430       gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7431                                   p1.Y() + grad.X() ) };
7432       if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7433         return PointPos( POS_VERTEX, index0 );
7434
7435       // check if the point2D is at "vertex 2" zone
7436       gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7437                                   p2.Y() + grad.X() ) };
7438       if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7439         return PointPos( POS_VERTEX, index0 + 1);
7440     }
7441     double edgeEquation =
7442       ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7443     return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7444   }
7445 }
7446
7447 //=======================================================================
7448 /*!
7449  * \brief Return minimal distance from a point to a face
7450  *
7451  * Currently we ignore non-planarity and 2nd order of face
7452  */
7453 //=======================================================================
7454
7455 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7456                                       const gp_Pnt&        point )
7457 {
7458   double badDistance = -1;
7459   if ( !face ) return badDistance;
7460
7461   // coordinates of nodes (medium nodes, if any, ignored)
7462   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7463   vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7464   xyz.resize( face->NbCornerNodes()+1 );
7465
7466   // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7467   // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7468   gp_Trsf trsf;
7469   gp_Vec OZ ( xyz[0], xyz[1] );
7470   gp_Vec OX ( xyz[0], xyz[2] );
7471   if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7472   {
7473     if ( xyz.size() < 4 ) return badDistance;
7474     OZ = gp_Vec ( xyz[0], xyz[2] );
7475     OX = gp_Vec ( xyz[0], xyz[3] );
7476   }
7477   gp_Ax3 tgtCS;
7478   try {
7479     tgtCS = gp_Ax3( xyz[0], OZ, OX );
7480   }
7481   catch ( Standard_Failure ) {
7482     return badDistance;
7483   }
7484   trsf.SetTransformation( tgtCS );
7485
7486   // move all the nodes to 2D
7487   vector<gp_XY> xy( xyz.size() );
7488   for ( size_t i = 0;i < xyz.size()-1; ++i )
7489   {
7490     gp_XYZ p3d = xyz[i];
7491     trsf.Transforms( p3d );
7492     xy[i].SetCoord( p3d.X(), p3d.Z() );
7493   }
7494   xyz.back() = xyz.front();
7495   xy.back() = xy.front();
7496
7497   // // move the point in 2D
7498   gp_XYZ tmpPnt = point.XYZ();
7499   trsf.Transforms( tmpPnt );
7500   gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7501
7502   // loop on segments of the face to analyze point position ralative to the face
7503   set< PointPos > pntPosSet;
7504   for ( size_t i = 1; i < xy.size(); ++i )
7505   {
7506     PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7507     pntPosSet.insert( pos );
7508   }
7509
7510   // compute distance
7511   PointPos pos = *pntPosSet.begin();
7512   // cout << "Face " << face->GetID() << " DIST: ";
7513   switch ( pos._name )
7514   {
7515   case POS_LEFT: {
7516     // point is most close to a segment
7517     gp_Vec p0p1( point, xyz[ pos._index ] );
7518     gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7519     p1p2.Normalize();
7520     double projDist = p0p1 * p1p2; // distance projected to the segment
7521     gp_Vec projVec = p1p2 * projDist;
7522     gp_Vec distVec = p0p1 - projVec;
7523     // cout << distVec.Magnitude()  << ", SEG " << face->GetNode(pos._index)->GetID()
7524     //      << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7525     return distVec.Magnitude();
7526   }
7527   case POS_RIGHT: {
7528     // point is inside the face
7529     double distToFacePlane = tmpPnt.Y();
7530     // cout << distToFacePlane << ", INSIDE " << endl;
7531     return Abs( distToFacePlane );
7532   }
7533   case POS_VERTEX: {
7534     // point is most close to a node
7535     gp_Vec distVec( point, xyz[ pos._index ]);
7536     // cout << distVec.Magnitude()  << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7537     return distVec.Magnitude();
7538   }
7539   }
7540   return badDistance;
7541 }
7542
7543 //=======================================================================
7544 //function : SimplifyFace
7545 //purpose  :
7546 //=======================================================================
7547 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7548                                     vector<const SMDS_MeshNode *>&      poly_nodes,
7549                                     vector<int>&                        quantities) const
7550 {
7551   int nbNodes = faceNodes.size();
7552
7553   if (nbNodes < 3)
7554     return 0;
7555
7556   set<const SMDS_MeshNode*> nodeSet;
7557
7558   // get simple seq of nodes
7559   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7560   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7561   int iSimple = 0, nbUnique = 0;
7562
7563   simpleNodes[iSimple++] = faceNodes[0];
7564   nbUnique++;
7565   for (int iCur = 1; iCur < nbNodes; iCur++) {
7566     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7567       simpleNodes[iSimple++] = faceNodes[iCur];
7568       if (nodeSet.insert( faceNodes[iCur] ).second)
7569         nbUnique++;
7570     }
7571   }
7572   int nbSimple = iSimple;
7573   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7574     nbSimple--;
7575     iSimple--;
7576   }
7577
7578   if (nbUnique < 3)
7579     return 0;
7580
7581   // separate loops
7582   int nbNew = 0;
7583   bool foundLoop = (nbSimple > nbUnique);
7584   while (foundLoop) {
7585     foundLoop = false;
7586     set<const SMDS_MeshNode*> loopSet;
7587     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7588       const SMDS_MeshNode* n = simpleNodes[iSimple];
7589       if (!loopSet.insert( n ).second) {
7590         foundLoop = true;
7591
7592         // separate loop
7593         int iC = 0, curLast = iSimple;
7594         for (; iC < curLast; iC++) {
7595           if (simpleNodes[iC] == n) break;
7596         }
7597         int loopLen = curLast - iC;
7598         if (loopLen > 2) {
7599           // create sub-element
7600           nbNew++;
7601           quantities.push_back(loopLen);
7602           for (; iC < curLast; iC++) {
7603             poly_nodes.push_back(simpleNodes[iC]);
7604           }
7605         }
7606         // shift the rest nodes (place from the first loop position)
7607         for (iC = curLast + 1; iC < nbSimple; iC++) {
7608           simpleNodes[iC - loopLen] = simpleNodes[iC];
7609         }
7610         nbSimple -= loopLen;
7611         iSimple -= loopLen;
7612       }
7613     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7614   } // while (foundLoop)
7615
7616   if (iSimple > 2) {
7617     nbNew++;
7618     quantities.push_back(iSimple);
7619     for (int i = 0; i < iSimple; i++)
7620       poly_nodes.push_back(simpleNodes[i]);
7621   }
7622
7623   return nbNew;
7624 }
7625
7626 //=======================================================================
7627 //function : MergeNodes
7628 //purpose  : In each group, the cdr of nodes are substituted by the first one
7629 //           in all elements.
7630 //=======================================================================
7631
7632 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7633 {
7634   MESSAGE("MergeNodes");
7635   myLastCreatedElems.Clear();
7636   myLastCreatedNodes.Clear();
7637
7638   SMESHDS_Mesh* aMesh = GetMeshDS();
7639
7640   TNodeNodeMap nodeNodeMap; // node to replace - new node
7641   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7642   list< int > rmElemIds, rmNodeIds;
7643
7644   // Fill nodeNodeMap and elems
7645
7646   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7647   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7648     list<const SMDS_MeshNode*>& nodes = *grIt;
7649     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7650     const SMDS_MeshNode* nToKeep = *nIt;
7651     //MESSAGE("node to keep " << nToKeep->GetID());
7652     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7653       const SMDS_MeshNode* nToRemove = *nIt;
7654       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7655       if ( nToRemove != nToKeep ) {
7656         //MESSAGE("  node to remove " << nToRemove->GetID());
7657         rmNodeIds.push_back( nToRemove->GetID() );
7658         AddToSameGroups( nToKeep, nToRemove, aMesh );
7659       }
7660
7661       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7662       while ( invElemIt->more() ) {
7663         const SMDS_MeshElement* elem = invElemIt->next();
7664         elems.insert(elem);
7665       }
7666     }
7667   }
7668   // Change element nodes or remove an element
7669
7670   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7671   for ( ; eIt != elems.end(); eIt++ ) {
7672     const SMDS_MeshElement* elem = *eIt;
7673     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7674     int nbNodes = elem->NbNodes();
7675     int aShapeId = FindShape( elem );
7676
7677     set<const SMDS_MeshNode*> nodeSet;
7678     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7679     int iUnique = 0, iCur = 0, nbRepl = 0;
7680     vector<int> iRepl( nbNodes );
7681
7682     // get new seq of nodes
7683     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7684     while ( itN->more() ) {
7685       const SMDS_MeshNode* n =
7686         static_cast<const SMDS_MeshNode*>( itN->next() );
7687
7688       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7689       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7690         n = (*nnIt).second;
7691         // BUG 0020185: begin
7692         {
7693           bool stopRecur = false;
7694           set<const SMDS_MeshNode*> nodesRecur;
7695           nodesRecur.insert(n);
7696           while (!stopRecur) {
7697             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7698             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7699               n = (*nnIt_i).second;
7700               if (!nodesRecur.insert(n).second) {
7701                 // error: recursive dependancy
7702                 stopRecur = true;
7703               }
7704             }
7705             else
7706               stopRecur = true;
7707           }
7708         }
7709         // BUG 0020185: end
7710       }
7711       curNodes[ iCur ] = n;
7712       bool isUnique = nodeSet.insert( n ).second;
7713       if ( isUnique )
7714         uniqueNodes[ iUnique++ ] = n;
7715       else
7716         iRepl[ nbRepl++ ] = iCur;
7717       iCur++;
7718     }
7719
7720     // Analyse element topology after replacement
7721
7722     bool isOk = true;
7723     int nbUniqueNodes = nodeSet.size();
7724     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7725     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7726       // Polygons and Polyhedral volumes
7727       if (elem->IsPoly()) {
7728
7729         if (elem->GetType() == SMDSAbs_Face) {
7730           // Polygon
7731           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7732           int inode = 0;
7733           for (; inode < nbNodes; inode++) {
7734             face_nodes[inode] = curNodes[inode];
7735           }
7736
7737           vector<const SMDS_MeshNode *> polygons_nodes;
7738           vector<int> quantities;
7739           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7740           if (nbNew > 0) {
7741             inode = 0;
7742             for (int iface = 0; iface < nbNew; iface++) {
7743               int nbNodes = quantities[iface];
7744               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7745               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7746                 poly_nodes[ii] = polygons_nodes[inode];
7747               }
7748               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7749               myLastCreatedElems.Append(newElem);
7750               if (aShapeId)
7751                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7752             }
7753
7754             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7755             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7756             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7757             int quid =0;
7758             if (nbNew > 0) quid = nbNew - 1;
7759             vector<int> newquant(quantities.begin()+quid, quantities.end());
7760             const SMDS_MeshElement* newElem = 0;
7761             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7762             myLastCreatedElems.Append(newElem);
7763             if ( aShapeId && newElem )
7764               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7765             rmElemIds.push_back(elem->GetID());
7766           }
7767           else {
7768             rmElemIds.push_back(elem->GetID());
7769           }
7770
7771         }
7772         else if (elem->GetType() == SMDSAbs_Volume) {
7773           // Polyhedral volume
7774           if (nbUniqueNodes < 4) {
7775             rmElemIds.push_back(elem->GetID());
7776           }
7777           else {
7778             // each face has to be analyzed in order to check volume validity
7779             const SMDS_VtkVolume* aPolyedre =
7780               dynamic_cast<const SMDS_VtkVolume*>( elem );
7781             if (aPolyedre) {
7782               int nbFaces = aPolyedre->NbFaces();
7783
7784               vector<const SMDS_MeshNode *> poly_nodes;
7785               vector<int> quantities;
7786
7787               for (int iface = 1; iface <= nbFaces; iface++) {
7788                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7789                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7790
7791                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7792                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7793                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7794                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7795                     faceNode = (*nnIt).second;
7796                   }
7797                   faceNodes[inode - 1] = faceNode;
7798                 }
7799
7800                 SimplifyFace(faceNodes, poly_nodes, quantities);
7801               }
7802
7803               if (quantities.size() > 3) {
7804                 // to be done: remove coincident faces
7805               }
7806
7807               if (quantities.size() > 3)
7808                 {
7809                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7810                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7811                   const SMDS_MeshElement* newElem = 0;
7812                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7813                   myLastCreatedElems.Append(newElem);
7814                   if ( aShapeId && newElem )
7815                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7816                   rmElemIds.push_back(elem->GetID());
7817                 }
7818             }
7819             else {
7820               rmElemIds.push_back(elem->GetID());
7821             }
7822           }
7823         }
7824         else {
7825         }
7826
7827         continue;
7828       } // poly element
7829
7830       // Regular elements
7831       // TODO not all the possible cases are solved. Find something more generic?
7832       switch ( nbNodes ) {
7833       case 2: ///////////////////////////////////// EDGE
7834         isOk = false; break;
7835       case 3: ///////////////////////////////////// TRIANGLE
7836         isOk = false; break;
7837       case 4:
7838         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7839           isOk = false;
7840         else { //////////////////////////////////// QUADRANGLE
7841           if ( nbUniqueNodes < 3 )
7842             isOk = false;
7843           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7844             isOk = false; // opposite nodes stick
7845           //MESSAGE("isOk " << isOk);
7846         }
7847         break;
7848       case 6: ///////////////////////////////////// PENTAHEDRON
7849         if ( nbUniqueNodes == 4 ) {
7850           // ---------------------------------> tetrahedron
7851           if (nbRepl == 3 &&
7852               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7853             // all top nodes stick: reverse a bottom
7854             uniqueNodes[ 0 ] = curNodes [ 1 ];
7855             uniqueNodes[ 1 ] = curNodes [ 0 ];
7856           }
7857           else if (nbRepl == 3 &&
7858                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7859             // all bottom nodes stick: set a top before
7860             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7861             uniqueNodes[ 0 ] = curNodes [ 3 ];
7862             uniqueNodes[ 1 ] = curNodes [ 4 ];
7863             uniqueNodes[ 2 ] = curNodes [ 5 ];
7864           }
7865           else if (nbRepl == 4 &&
7866                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7867             // a lateral face turns into a line: reverse a bottom
7868             uniqueNodes[ 0 ] = curNodes [ 1 ];
7869             uniqueNodes[ 1 ] = curNodes [ 0 ];
7870           }
7871           else
7872             isOk = false;
7873         }
7874         else if ( nbUniqueNodes == 5 ) {
7875           // PENTAHEDRON --------------------> 2 tetrahedrons
7876           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7877             // a bottom node sticks with a linked top one
7878             // 1.
7879             SMDS_MeshElement* newElem =
7880               aMesh->AddVolume(curNodes[ 3 ],
7881                                curNodes[ 4 ],
7882                                curNodes[ 5 ],
7883                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7884             myLastCreatedElems.Append(newElem);
7885             if ( aShapeId )
7886               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7887             // 2. : reverse a bottom
7888             uniqueNodes[ 0 ] = curNodes [ 1 ];
7889             uniqueNodes[ 1 ] = curNodes [ 0 ];
7890             nbUniqueNodes = 4;
7891           }
7892           else
7893             isOk = false;
7894         }
7895         else
7896           isOk = false;
7897         break;
7898       case 8: {
7899         if(elem->IsQuadratic()) { // Quadratic quadrangle
7900           //   1    5    2
7901           //    +---+---+
7902           //    |       |
7903           //    |       |
7904           //   4+       +6
7905           //    |       |
7906           //    |       |
7907           //    +---+---+
7908           //   0    7    3
7909           isOk = false;
7910           if(nbRepl==2) {
7911             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7912           }
7913           if(nbRepl==3) {
7914             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7915             nbUniqueNodes = 6;
7916             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7917               uniqueNodes[0] = curNodes[0];
7918               uniqueNodes[1] = curNodes[2];
7919               uniqueNodes[2] = curNodes[3];
7920               uniqueNodes[3] = curNodes[5];
7921               uniqueNodes[4] = curNodes[6];
7922               uniqueNodes[5] = curNodes[7];
7923               isOk = true;
7924             }
7925             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7926               uniqueNodes[0] = curNodes[0];
7927               uniqueNodes[1] = curNodes[1];
7928               uniqueNodes[2] = curNodes[2];
7929               uniqueNodes[3] = curNodes[4];
7930               uniqueNodes[4] = curNodes[5];
7931               uniqueNodes[5] = curNodes[6];
7932               isOk = true;
7933             }
7934             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7935               uniqueNodes[0] = curNodes[1];
7936               uniqueNodes[1] = curNodes[2];
7937               uniqueNodes[2] = curNodes[3];
7938               uniqueNodes[3] = curNodes[5];
7939               uniqueNodes[4] = curNodes[6];
7940               uniqueNodes[5] = curNodes[0];
7941               isOk = true;
7942             }
7943             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7944               uniqueNodes[0] = curNodes[0];
7945               uniqueNodes[1] = curNodes[1];
7946               uniqueNodes[2] = curNodes[3];
7947               uniqueNodes[3] = curNodes[4];
7948               uniqueNodes[4] = curNodes[6];
7949               uniqueNodes[5] = curNodes[7];
7950               isOk = true;
7951             }
7952             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7953               uniqueNodes[0] = curNodes[0];
7954               uniqueNodes[1] = curNodes[2];
7955               uniqueNodes[2] = curNodes[3];
7956               uniqueNodes[3] = curNodes[1];
7957               uniqueNodes[4] = curNodes[6];
7958               uniqueNodes[5] = curNodes[7];
7959               isOk = true;
7960             }
7961             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7962               uniqueNodes[0] = curNodes[0];
7963               uniqueNodes[1] = curNodes[1];
7964               uniqueNodes[2] = curNodes[2];
7965               uniqueNodes[3] = curNodes[4];
7966               uniqueNodes[4] = curNodes[5];
7967               uniqueNodes[5] = curNodes[7];
7968               isOk = true;
7969             }
7970             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7971               uniqueNodes[0] = curNodes[0];
7972               uniqueNodes[1] = curNodes[1];
7973               uniqueNodes[2] = curNodes[3];
7974               uniqueNodes[3] = curNodes[4];
7975               uniqueNodes[4] = curNodes[2];
7976               uniqueNodes[5] = curNodes[7];
7977               isOk = true;
7978             }
7979             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7980               uniqueNodes[0] = curNodes[0];
7981               uniqueNodes[1] = curNodes[1];
7982               uniqueNodes[2] = curNodes[2];
7983               uniqueNodes[3] = curNodes[4];
7984               uniqueNodes[4] = curNodes[5];
7985               uniqueNodes[5] = curNodes[3];
7986               isOk = true;
7987             }
7988           }
7989           if(nbRepl==4) {
7990             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7991           }
7992           if(nbRepl==5) {
7993             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7994           }
7995           break;
7996         }
7997         //////////////////////////////////// HEXAHEDRON
7998         isOk = false;
7999         SMDS_VolumeTool hexa (elem);
8000         hexa.SetExternalNormal();
8001         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8002           //////////////////////// HEX ---> 1 tetrahedron
8003           for ( int iFace = 0; iFace < 6; iFace++ ) {
8004             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8005             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8006                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8007                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8008               // one face turns into a point ...
8009               int iOppFace = hexa.GetOppFaceIndex( iFace );
8010               ind = hexa.GetFaceNodesIndices( iOppFace );
8011               int nbStick = 0;
8012               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8013                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8014                   nbStick++;
8015               }
8016               if ( nbStick == 1 ) {
8017                 // ... and the opposite one - into a triangle.
8018                 // set a top node
8019                 ind = hexa.GetFaceNodesIndices( iFace );
8020                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8021                 isOk = true;
8022               }
8023               break;
8024             }
8025           }
8026         }
8027         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8028           //////////////////////// HEX ---> 1 prism
8029           int nbTria = 0, iTria[3];
8030           const int *ind; // indices of face nodes
8031           // look for triangular faces
8032           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8033             ind = hexa.GetFaceNodesIndices( iFace );
8034             TIDSortedNodeSet faceNodes;
8035             for ( iCur = 0; iCur < 4; iCur++ )
8036               faceNodes.insert( curNodes[ind[iCur]] );
8037             if ( faceNodes.size() == 3 )
8038               iTria[ nbTria++ ] = iFace;
8039           }
8040           // check if triangles are opposite
8041           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8042           {
8043             isOk = true;
8044             // set nodes of the bottom triangle
8045             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8046             vector<int> indB;
8047             for ( iCur = 0; iCur < 4; iCur++ )
8048               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8049                 indB.push_back( ind[iCur] );
8050             if ( !hexa.IsForward() )
8051               std::swap( indB[0], indB[2] );
8052             for ( iCur = 0; iCur < 3; iCur++ )
8053               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8054             // set nodes of the top triangle
8055             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8056             for ( iCur = 0; iCur < 3; ++iCur )
8057               for ( int j = 0; j < 4; ++j )
8058                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8059                 {
8060                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8061                   break;
8062                 }
8063           }
8064           break;
8065         }
8066         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8067           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8068           for ( int iFace = 0; iFace < 6; iFace++ ) {
8069             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8070             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8071                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8072                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8073               // one face turns into a point ...
8074               int iOppFace = hexa.GetOppFaceIndex( iFace );
8075               ind = hexa.GetFaceNodesIndices( iOppFace );
8076               int nbStick = 0;
8077               iUnique = 2;  // reverse a tetrahedron 1 bottom
8078               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8079                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8080                   nbStick++;
8081                 else if ( iUnique >= 0 )
8082                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8083               }
8084               if ( nbStick == 0 ) {
8085                 // ... and the opposite one is a quadrangle
8086                 // set a top node
8087                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8088                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8089                 nbUniqueNodes = 4;
8090                 // tetrahedron 2
8091                 SMDS_MeshElement* newElem =
8092                   aMesh->AddVolume(curNodes[ind[ 0 ]],
8093                                    curNodes[ind[ 3 ]],
8094                                    curNodes[ind[ 2 ]],
8095                                    curNodes[indTop[ 0 ]]);
8096                 myLastCreatedElems.Append(newElem);
8097                 if ( aShapeId )
8098                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8099                 isOk = true;
8100               }
8101               break;
8102             }
8103           }
8104         }
8105         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8106           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8107           // find indices of quad and tri faces
8108           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8109           for ( iFace = 0; iFace < 6; iFace++ ) {
8110             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8111             nodeSet.clear();
8112             for ( iCur = 0; iCur < 4; iCur++ )
8113               nodeSet.insert( curNodes[ind[ iCur ]] );
8114             nbUniqueNodes = nodeSet.size();
8115             if ( nbUniqueNodes == 3 )
8116               iTriFace[ nbTri++ ] = iFace;
8117             else if ( nbUniqueNodes == 4 )
8118               iQuadFace[ nbQuad++ ] = iFace;
8119           }
8120           if (nbQuad == 2 && nbTri == 4 &&
8121               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8122             // 2 opposite quadrangles stuck with a diagonal;
8123             // sample groups of merged indices: (0-4)(2-6)
8124             // --------------------------------------------> 2 tetrahedrons
8125             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8126             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8127             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8128             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8129                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8130               // stuck with 0-2 diagonal
8131               i0  = ind1[ 3 ];
8132               i1d = ind1[ 0 ];
8133               i2  = ind1[ 1 ];
8134               i3d = ind1[ 2 ];
8135               i0t = ind2[ 1 ];
8136               i2t = ind2[ 3 ];
8137             }
8138             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8139                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8140               // stuck with 1-3 diagonal
8141               i0  = ind1[ 0 ];
8142               i1d = ind1[ 1 ];
8143               i2  = ind1[ 2 ];
8144               i3d = ind1[ 3 ];
8145               i0t = ind2[ 0 ];
8146               i2t = ind2[ 1 ];
8147             }
8148             else {
8149               ASSERT(0);
8150             }
8151             // tetrahedron 1
8152             uniqueNodes[ 0 ] = curNodes [ i0 ];
8153             uniqueNodes[ 1 ] = curNodes [ i1d ];
8154             uniqueNodes[ 2 ] = curNodes [ i3d ];
8155             uniqueNodes[ 3 ] = curNodes [ i0t ];
8156             nbUniqueNodes = 4;
8157             // tetrahedron 2
8158             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8159                                                          curNodes[ i2 ],
8160                                                          curNodes[ i3d ],
8161                                                          curNodes[ i2t ]);
8162             myLastCreatedElems.Append(newElem);
8163             if ( aShapeId )
8164               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8165             isOk = true;
8166           }
8167           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8168                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8169             // --------------------------------------------> prism
8170             // find 2 opposite triangles
8171             nbUniqueNodes = 6;
8172             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8173               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8174                 // find indices of kept and replaced nodes
8175                 // and fill unique nodes of 2 opposite triangles
8176                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8177                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8178                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8179                 // fill unique nodes
8180                 iUnique = 0;
8181                 isOk = true;
8182                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8183                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8184                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8185                   if ( n == nInit ) {
8186                     // iCur of a linked node of the opposite face (make normals co-directed):
8187                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8188                     // check that correspondent corners of triangles are linked
8189                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8190                       isOk = false;
8191                     else {
8192                       uniqueNodes[ iUnique ] = n;
8193                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8194                       iUnique++;
8195                     }
8196                   }
8197                 }
8198                 break;
8199               }
8200             }
8201           }
8202         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8203         else
8204         {
8205           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8206         }
8207         break;
8208       } // HEXAHEDRON
8209
8210       default:
8211         isOk = false;
8212       } // switch ( nbNodes )
8213
8214     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8215
8216     if ( isOk ) { // the elem remains valid after sticking nodes
8217       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8218       {
8219         // Change nodes of polyedre
8220         const SMDS_VtkVolume* aPolyedre =
8221           dynamic_cast<const SMDS_VtkVolume*>( elem );
8222         if (aPolyedre) {
8223           int nbFaces = aPolyedre->NbFaces();
8224
8225           vector<const SMDS_MeshNode *> poly_nodes;
8226           vector<int> quantities (nbFaces);
8227
8228           for (int iface = 1; iface <= nbFaces; iface++) {
8229             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8230             quantities[iface - 1] = nbFaceNodes;
8231
8232             for (inode = 1; inode <= nbFaceNodes; inode++) {
8233               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8234
8235               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8236               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8237                 curNode = (*nnIt).second;
8238               }
8239               poly_nodes.push_back(curNode);
8240             }
8241           }
8242           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8243         }
8244       }
8245       else // replace non-polyhedron elements
8246       {
8247         const SMDSAbs_ElementType etyp = elem->GetType();
8248         const int elemId               = elem->GetID();
8249         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8250         uniqueNodes.resize(nbUniqueNodes);
8251
8252         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8253
8254         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8255         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8256         if ( sm && newElem )
8257           sm->AddElement( newElem );
8258         if ( elem != newElem )
8259           ReplaceElemInGroups( elem, newElem, aMesh );
8260       }
8261     }
8262     else {
8263       // Remove invalid regular element or invalid polygon
8264       rmElemIds.push_back( elem->GetID() );
8265     }
8266
8267   } // loop on elements
8268
8269   // Remove bad elements, then equal nodes (order important)
8270
8271   Remove( rmElemIds, false );
8272   Remove( rmNodeIds, true );
8273
8274 }
8275
8276
8277 // ========================================================
8278 // class   : SortableElement
8279 // purpose : allow sorting elements basing on their nodes
8280 // ========================================================
8281 class SortableElement : public set <const SMDS_MeshElement*>
8282 {
8283 public:
8284
8285   SortableElement( const SMDS_MeshElement* theElem )
8286   {
8287     myElem = theElem;
8288     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8289     while ( nodeIt->more() )
8290       this->insert( nodeIt->next() );
8291   }
8292
8293   const SMDS_MeshElement* Get() const
8294   { return myElem; }
8295
8296   void Set(const SMDS_MeshElement* e) const
8297   { myElem = e; }
8298
8299
8300 private:
8301   mutable const SMDS_MeshElement* myElem;
8302 };
8303
8304 //=======================================================================
8305 //function : FindEqualElements
8306 //purpose  : Return list of group of elements built on the same nodes.
8307 //           Search among theElements or in the whole mesh if theElements is empty
8308 //=======================================================================
8309
8310 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8311                                          TListOfListOfElementsID & theGroupsOfElementsID)
8312 {
8313   myLastCreatedElems.Clear();
8314   myLastCreatedNodes.Clear();
8315
8316   typedef map< SortableElement, int > TMapOfNodeSet;
8317   typedef list<int> TGroupOfElems;
8318
8319   if ( theElements.empty() )
8320   { // get all elements in the mesh
8321     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8322     while ( eIt->more() )
8323       theElements.insert( theElements.end(), eIt->next());
8324   }
8325
8326   vector< TGroupOfElems > arrayOfGroups;
8327   TGroupOfElems groupOfElems;
8328   TMapOfNodeSet mapOfNodeSet;
8329
8330   TIDSortedElemSet::iterator elemIt = theElements.begin();
8331   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8332     const SMDS_MeshElement* curElem = *elemIt;
8333     SortableElement SE(curElem);
8334     int ind = -1;
8335     // check uniqueness
8336     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8337     if( !(pp.second) ) {
8338       TMapOfNodeSet::iterator& itSE = pp.first;
8339       ind = (*itSE).second;
8340       arrayOfGroups[ind].push_back(curElem->GetID());
8341     }
8342     else {
8343       groupOfElems.clear();
8344       groupOfElems.push_back(curElem->GetID());
8345       arrayOfGroups.push_back(groupOfElems);
8346       i++;
8347     }
8348   }
8349
8350   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8351   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8352     groupOfElems = *groupIt;
8353     if ( groupOfElems.size() > 1 ) {
8354       groupOfElems.sort();
8355       theGroupsOfElementsID.push_back(groupOfElems);
8356     }
8357   }
8358 }
8359
8360 //=======================================================================
8361 //function : MergeElements
8362 //purpose  : In each given group, substitute all elements by the first one.
8363 //=======================================================================
8364
8365 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8366 {
8367   myLastCreatedElems.Clear();
8368   myLastCreatedNodes.Clear();
8369
8370   typedef list<int> TListOfIDs;
8371   TListOfIDs rmElemIds; // IDs of elems to remove
8372
8373   SMESHDS_Mesh* aMesh = GetMeshDS();
8374
8375   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8376   while ( groupsIt != theGroupsOfElementsID.end() ) {
8377     TListOfIDs& aGroupOfElemID = *groupsIt;
8378     aGroupOfElemID.sort();
8379     int elemIDToKeep = aGroupOfElemID.front();
8380     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8381     aGroupOfElemID.pop_front();
8382     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8383     while ( idIt != aGroupOfElemID.end() ) {
8384       int elemIDToRemove = *idIt;
8385       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8386       // add the kept element in groups of removed one (PAL15188)
8387       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8388       rmElemIds.push_back( elemIDToRemove );
8389       ++idIt;
8390     }
8391     ++groupsIt;
8392   }
8393
8394   Remove( rmElemIds, false );
8395 }
8396
8397 //=======================================================================
8398 //function : MergeEqualElements
8399 //purpose  : Remove all but one of elements built on the same nodes.
8400 //=======================================================================
8401
8402 void SMESH_MeshEditor::MergeEqualElements()
8403 {
8404   TIDSortedElemSet aMeshElements; /* empty input ==
8405                                      to merge equal elements in the whole mesh */
8406   TListOfListOfElementsID aGroupsOfElementsID;
8407   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8408   MergeElements(aGroupsOfElementsID);
8409 }
8410
8411 //=======================================================================
8412 //function : FindFaceInSet
8413 //purpose  : Return a face having linked nodes n1 and n2 and which is
8414 //           - not in avoidSet,
8415 //           - in elemSet provided that !elemSet.empty()
8416 //           i1 and i2 optionally returns indices of n1 and n2
8417 //=======================================================================
8418
8419 const SMDS_MeshElement*
8420 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8421                                 const SMDS_MeshNode*    n2,
8422                                 const TIDSortedElemSet& elemSet,
8423                                 const TIDSortedElemSet& avoidSet,
8424                                 int*                    n1ind,
8425                                 int*                    n2ind)
8426
8427 {
8428   int i1, i2;
8429   const SMDS_MeshElement* face = 0;
8430
8431   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8432   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8433   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8434   {
8435     //MESSAGE("in while ( invElemIt->more() && !face )");
8436     const SMDS_MeshElement* elem = invElemIt->next();
8437     if (avoidSet.count( elem ))
8438       continue;
8439     if ( !elemSet.empty() && !elemSet.count( elem ))
8440       continue;
8441     // index of n1
8442     i1 = elem->GetNodeIndex( n1 );
8443     // find a n2 linked to n1
8444     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8445     for ( int di = -1; di < 2 && !face; di += 2 )
8446     {
8447       i2 = (i1+di+nbN) % nbN;
8448       if ( elem->GetNode( i2 ) == n2 )
8449         face = elem;
8450     }
8451     if ( !face && elem->IsQuadratic())
8452     {
8453       // analysis for quadratic elements using all nodes
8454       const SMDS_VtkFace* F =
8455         dynamic_cast<const SMDS_VtkFace*>(elem);
8456       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8457       // use special nodes iterator
8458       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8459       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8460       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8461       {
8462         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8463         if ( n1 == prevN && n2 == n )
8464         {
8465           face = elem;
8466         }
8467         else if ( n2 == prevN && n1 == n )
8468         {
8469           face = elem; swap( i1, i2 );
8470         }
8471         prevN = n;
8472       }
8473     }
8474   }
8475   if ( n1ind ) *n1ind = i1;
8476   if ( n2ind ) *n2ind = i2;
8477   return face;
8478 }
8479
8480 //=======================================================================
8481 //function : findAdjacentFace
8482 //purpose  :
8483 //=======================================================================
8484
8485 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8486                                                 const SMDS_MeshNode* n2,
8487                                                 const SMDS_MeshElement* elem)
8488 {
8489   TIDSortedElemSet elemSet, avoidSet;
8490   if ( elem )
8491     avoidSet.insert ( elem );
8492   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8493 }
8494
8495 //=======================================================================
8496 //function : FindFreeBorder
8497 //purpose  :
8498 //=======================================================================
8499
8500 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8501
8502 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8503                                        const SMDS_MeshNode*             theSecondNode,
8504                                        const SMDS_MeshNode*             theLastNode,
8505                                        list< const SMDS_MeshNode* > &   theNodes,
8506                                        list< const SMDS_MeshElement* >& theFaces)
8507 {
8508   if ( !theFirstNode || !theSecondNode )
8509     return false;
8510   // find border face between theFirstNode and theSecondNode
8511   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8512   if ( !curElem )
8513     return false;
8514
8515   theFaces.push_back( curElem );
8516   theNodes.push_back( theFirstNode );
8517   theNodes.push_back( theSecondNode );
8518
8519   //vector<const SMDS_MeshNode*> nodes;
8520   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8521   TIDSortedElemSet foundElems;
8522   bool needTheLast = ( theLastNode != 0 );
8523
8524   while ( nStart != theLastNode ) {
8525     if ( nStart == theFirstNode )
8526       return !needTheLast;
8527
8528     // find all free border faces sharing form nStart
8529
8530     list< const SMDS_MeshElement* > curElemList;
8531     list< const SMDS_MeshNode* > nStartList;
8532     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8533     while ( invElemIt->more() ) {
8534       const SMDS_MeshElement* e = invElemIt->next();
8535       if ( e == curElem || foundElems.insert( e ).second ) {
8536         // get nodes
8537         int iNode = 0, nbNodes = e->NbNodes();
8538         //const SMDS_MeshNode* nodes[nbNodes+1];
8539         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8540
8541         if(e->IsQuadratic()) {
8542           const SMDS_VtkFace* F =
8543             dynamic_cast<const SMDS_VtkFace*>(e);
8544           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8545           // use special nodes iterator
8546           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8547           while( anIter->more() ) {
8548             nodes[ iNode++ ] = cast2Node(anIter->next());
8549           }
8550         }
8551         else {
8552           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8553           while ( nIt->more() )
8554             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8555         }
8556         nodes[ iNode ] = nodes[ 0 ];
8557         // check 2 links
8558         for ( iNode = 0; iNode < nbNodes; iNode++ )
8559           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8560                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8561               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8562           {
8563             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8564             curElemList.push_back( e );
8565           }
8566       }
8567     }
8568     // analyse the found
8569
8570     int nbNewBorders = curElemList.size();
8571     if ( nbNewBorders == 0 ) {
8572       // no free border furthermore
8573       return !needTheLast;
8574     }
8575     else if ( nbNewBorders == 1 ) {
8576       // one more element found
8577       nIgnore = nStart;
8578       nStart = nStartList.front();
8579       curElem = curElemList.front();
8580       theFaces.push_back( curElem );
8581       theNodes.push_back( nStart );
8582     }
8583     else {
8584       // several continuations found
8585       list< const SMDS_MeshElement* >::iterator curElemIt;
8586       list< const SMDS_MeshNode* >::iterator nStartIt;
8587       // check if one of them reached the last node
8588       if ( needTheLast ) {
8589         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8590              curElemIt!= curElemList.end();
8591              curElemIt++, nStartIt++ )
8592           if ( *nStartIt == theLastNode ) {
8593             theFaces.push_back( *curElemIt );
8594             theNodes.push_back( *nStartIt );
8595             return true;
8596           }
8597       }
8598       // find the best free border by the continuations
8599       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8600       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8601       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8602            curElemIt!= curElemList.end();
8603            curElemIt++, nStartIt++ )
8604       {
8605         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8606         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8607         // find one more free border
8608         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8609           cNL->clear();
8610           cFL->clear();
8611         }
8612         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8613           // choice: clear a worse one
8614           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8615           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8616           contNodes[ iWorse ].clear();
8617           contFaces[ iWorse ].clear();
8618         }
8619       }
8620       if ( contNodes[0].empty() && contNodes[1].empty() )
8621         return false;
8622
8623       // append the best free border
8624       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8625       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8626       theNodes.pop_back(); // remove nIgnore
8627       theNodes.pop_back(); // remove nStart
8628       theFaces.pop_back(); // remove curElem
8629       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8630       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8631       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8632       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8633       return true;
8634
8635     } // several continuations found
8636   } // while ( nStart != theLastNode )
8637
8638   return true;
8639 }
8640
8641 //=======================================================================
8642 //function : CheckFreeBorderNodes
8643 //purpose  : Return true if the tree nodes are on a free border
8644 //=======================================================================
8645
8646 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8647                                             const SMDS_MeshNode* theNode2,
8648                                             const SMDS_MeshNode* theNode3)
8649 {
8650   list< const SMDS_MeshNode* > nodes;
8651   list< const SMDS_MeshElement* > faces;
8652   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8653 }
8654
8655 //=======================================================================
8656 //function : SewFreeBorder
8657 //purpose  :
8658 //=======================================================================
8659
8660 SMESH_MeshEditor::Sew_Error
8661 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8662                                  const SMDS_MeshNode* theBordSecondNode,
8663                                  const SMDS_MeshNode* theBordLastNode,
8664                                  const SMDS_MeshNode* theSideFirstNode,
8665                                  const SMDS_MeshNode* theSideSecondNode,
8666                                  const SMDS_MeshNode* theSideThirdNode,
8667                                  const bool           theSideIsFreeBorder,
8668                                  const bool           toCreatePolygons,
8669                                  const bool           toCreatePolyedrs)
8670 {
8671   myLastCreatedElems.Clear();
8672   myLastCreatedNodes.Clear();
8673
8674   MESSAGE("::SewFreeBorder()");
8675   Sew_Error aResult = SEW_OK;
8676
8677   // ====================================
8678   //    find side nodes and elements
8679   // ====================================
8680
8681   list< const SMDS_MeshNode* > nSide[ 2 ];
8682   list< const SMDS_MeshElement* > eSide[ 2 ];
8683   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8684   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8685
8686   // Free border 1
8687   // --------------
8688   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8689                       nSide[0], eSide[0])) {
8690     MESSAGE(" Free Border 1 not found " );
8691     aResult = SEW_BORDER1_NOT_FOUND;
8692   }
8693   if (theSideIsFreeBorder) {
8694     // Free border 2
8695     // --------------
8696     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8697                         nSide[1], eSide[1])) {
8698       MESSAGE(" Free Border 2 not found " );
8699       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8700     }
8701   }
8702   if ( aResult != SEW_OK )
8703     return aResult;
8704
8705   if (!theSideIsFreeBorder) {
8706     // Side 2
8707     // --------------
8708
8709     // -------------------------------------------------------------------------
8710     // Algo:
8711     // 1. If nodes to merge are not coincident, move nodes of the free border
8712     //    from the coord sys defined by the direction from the first to last
8713     //    nodes of the border to the correspondent sys of the side 2
8714     // 2. On the side 2, find the links most co-directed with the correspondent
8715     //    links of the free border
8716     // -------------------------------------------------------------------------
8717
8718     // 1. Since sewing may break if there are volumes to split on the side 2,
8719     //    we wont move nodes but just compute new coordinates for them
8720     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8721     TNodeXYZMap nBordXYZ;
8722     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8723     list< const SMDS_MeshNode* >::iterator nBordIt;
8724
8725     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8726     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8727     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8728     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8729     double tol2 = 1.e-8;
8730     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8731     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8732       // Need node movement.
8733
8734       // find X and Z axes to create trsf
8735       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8736       gp_Vec X = Zs ^ Zb;
8737       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8738         // Zb || Zs
8739         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8740
8741       // coord systems
8742       gp_Ax3 toBordAx( Pb1, Zb, X );
8743       gp_Ax3 fromSideAx( Ps1, Zs, X );
8744       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8745       // set trsf
8746       gp_Trsf toBordSys, fromSide2Sys;
8747       toBordSys.SetTransformation( toBordAx );
8748       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8749       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8750
8751       // move
8752       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8753         const SMDS_MeshNode* n = *nBordIt;
8754         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8755         toBordSys.Transforms( xyz );
8756         fromSide2Sys.Transforms( xyz );
8757         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8758       }
8759     }
8760     else {
8761       // just insert nodes XYZ in the nBordXYZ map
8762       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8763         const SMDS_MeshNode* n = *nBordIt;
8764         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8765       }
8766     }
8767
8768     // 2. On the side 2, find the links most co-directed with the correspondent
8769     //    links of the free border
8770
8771     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8772     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8773     sideNodes.push_back( theSideFirstNode );
8774
8775     bool hasVolumes = false;
8776     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8777     set<long> foundSideLinkIDs, checkedLinkIDs;
8778     SMDS_VolumeTool volume;
8779     //const SMDS_MeshNode* faceNodes[ 4 ];
8780
8781     const SMDS_MeshNode*    sideNode;
8782     const SMDS_MeshElement* sideElem;
8783     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8784     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8785     nBordIt = bordNodes.begin();
8786     nBordIt++;
8787     // border node position and border link direction to compare with
8788     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8789     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8790     // choose next side node by link direction or by closeness to
8791     // the current border node:
8792     bool searchByDir = ( *nBordIt != theBordLastNode );
8793     do {
8794       // find the next node on the Side 2
8795       sideNode = 0;
8796       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8797       long linkID;
8798       checkedLinkIDs.clear();
8799       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8800
8801       // loop on inverse elements of current node (prevSideNode) on the Side 2
8802       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8803       while ( invElemIt->more() )
8804       {
8805         const SMDS_MeshElement* elem = invElemIt->next();
8806         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8807         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8808         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8809         bool isVolume = volume.Set( elem );
8810         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8811         if ( isVolume ) // --volume
8812           hasVolumes = true;
8813         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8814           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8815           if(elem->IsQuadratic()) {
8816             const SMDS_VtkFace* F =
8817               dynamic_cast<const SMDS_VtkFace*>(elem);
8818             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8819             // use special nodes iterator
8820             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8821             while( anIter->more() ) {
8822               nodes[ iNode ] = cast2Node(anIter->next());
8823               if ( nodes[ iNode++ ] == prevSideNode )
8824                 iPrevNode = iNode - 1;
8825             }
8826           }
8827           else {
8828             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8829             while ( nIt->more() ) {
8830               nodes[ iNode ] = cast2Node( nIt->next() );
8831               if ( nodes[ iNode++ ] == prevSideNode )
8832                 iPrevNode = iNode - 1;
8833             }
8834           }
8835           // there are 2 links to check
8836           nbNodes = 2;
8837         }
8838         else // --edge
8839           continue;
8840         // loop on links, to be precise, on the second node of links
8841         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8842           const SMDS_MeshNode* n = nodes[ iNode ];
8843           if ( isVolume ) {
8844             if ( !volume.IsLinked( n, prevSideNode ))
8845               continue;
8846           }
8847           else {
8848             if ( iNode ) // a node before prevSideNode
8849               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8850             else         // a node after prevSideNode
8851               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8852           }
8853           // check if this link was already used
8854           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8855           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8856           if (!isJustChecked &&
8857               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8858           {
8859             // test a link geometrically
8860             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8861             bool linkIsBetter = false;
8862             double dot = 0.0, dist = 0.0;
8863             if ( searchByDir ) { // choose most co-directed link
8864               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8865               linkIsBetter = ( dot > maxDot );
8866             }
8867             else { // choose link with the node closest to bordPos
8868               dist = ( nextXYZ - bordPos ).SquareModulus();
8869               linkIsBetter = ( dist < minDist );
8870             }
8871             if ( linkIsBetter ) {
8872               maxDot = dot;
8873               minDist = dist;
8874               linkID = iLink;
8875               sideNode = n;
8876               sideElem = elem;
8877             }
8878           }
8879         }
8880       } // loop on inverse elements of prevSideNode
8881
8882       if ( !sideNode ) {
8883         MESSAGE(" Cant find path by links of the Side 2 ");
8884         return SEW_BAD_SIDE_NODES;
8885       }
8886       sideNodes.push_back( sideNode );
8887       sideElems.push_back( sideElem );
8888       foundSideLinkIDs.insert ( linkID );
8889       prevSideNode = sideNode;
8890
8891       if ( *nBordIt == theBordLastNode )
8892         searchByDir = false;
8893       else {
8894         // find the next border link to compare with
8895         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8896         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8897         // move to next border node if sideNode is before forward border node (bordPos)
8898         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8899           prevBordNode = *nBordIt;
8900           nBordIt++;
8901           bordPos = nBordXYZ[ *nBordIt ];
8902           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8903           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8904         }
8905       }
8906     }
8907     while ( sideNode != theSideSecondNode );
8908
8909     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8910       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8911       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8912     }
8913   } // end nodes search on the side 2
8914
8915   // ============================
8916   // sew the border to the side 2
8917   // ============================
8918
8919   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8920   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8921
8922   TListOfListOfNodes nodeGroupsToMerge;
8923   if ( nbNodes[0] == nbNodes[1] ||
8924        ( theSideIsFreeBorder && !theSideThirdNode)) {
8925
8926     // all nodes are to be merged
8927
8928     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8929          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8930          nIt[0]++, nIt[1]++ )
8931     {
8932       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8933       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8934       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8935     }
8936   }
8937   else {
8938
8939     // insert new nodes into the border and the side to get equal nb of segments
8940
8941     // get normalized parameters of nodes on the borders
8942     //double param[ 2 ][ maxNbNodes ];
8943     double* param[ 2 ];
8944     param[0] = new double [ maxNbNodes ];
8945     param[1] = new double [ maxNbNodes ];
8946     int iNode, iBord;
8947     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8948       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8949       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8950       const SMDS_MeshNode* nPrev = *nIt;
8951       double bordLength = 0;
8952       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8953         const SMDS_MeshNode* nCur = *nIt;
8954         gp_XYZ segment (nCur->X() - nPrev->X(),
8955                         nCur->Y() - nPrev->Y(),
8956                         nCur->Z() - nPrev->Z());
8957         double segmentLen = segment.Modulus();
8958         bordLength += segmentLen;
8959         param[ iBord ][ iNode ] = bordLength;
8960         nPrev = nCur;
8961       }
8962       // normalize within [0,1]
8963       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8964         param[ iBord ][ iNode ] /= bordLength;
8965       }
8966     }
8967
8968     // loop on border segments
8969     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8970     int i[ 2 ] = { 0, 0 };
8971     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8972     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8973
8974     TElemOfNodeListMap insertMap;
8975     TElemOfNodeListMap::iterator insertMapIt;
8976     // insertMap is
8977     // key:   elem to insert nodes into
8978     // value: 2 nodes to insert between + nodes to be inserted
8979     do {
8980       bool next[ 2 ] = { false, false };
8981
8982       // find min adjacent segment length after sewing
8983       double nextParam = 10., prevParam = 0;
8984       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8985         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8986           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8987         if ( i[ iBord ] > 0 )
8988           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8989       }
8990       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8991       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8992       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8993
8994       // choose to insert or to merge nodes
8995       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8996       if ( Abs( du ) <= minSegLen * 0.2 ) {
8997         // merge
8998         // ------
8999         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9000         const SMDS_MeshNode* n0 = *nIt[0];
9001         const SMDS_MeshNode* n1 = *nIt[1];
9002         nodeGroupsToMerge.back().push_back( n1 );
9003         nodeGroupsToMerge.back().push_back( n0 );
9004         // position of node of the border changes due to merge
9005         param[ 0 ][ i[0] ] += du;
9006         // move n1 for the sake of elem shape evaluation during insertion.
9007         // n1 will be removed by MergeNodes() anyway
9008         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9009         next[0] = next[1] = true;
9010       }
9011       else {
9012         // insert
9013         // ------
9014         int intoBord = ( du < 0 ) ? 0 : 1;
9015         const SMDS_MeshElement* elem = *eIt[ intoBord ];
9016         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
9017         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
9018         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
9019         if ( intoBord == 1 ) {
9020           // move node of the border to be on a link of elem of the side
9021           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9022           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9023           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9024           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9025           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9026         }
9027         insertMapIt = insertMap.find( elem );
9028         bool notFound = ( insertMapIt == insertMap.end() );
9029         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9030         if ( otherLink ) {
9031           // insert into another link of the same element:
9032           // 1. perform insertion into the other link of the elem
9033           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9034           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9035           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9036           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9037           // 2. perform insertion into the link of adjacent faces
9038           while (true) {
9039             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9040             if ( adjElem )
9041               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9042             else
9043               break;
9044           }
9045           if (toCreatePolyedrs) {
9046             // perform insertion into the links of adjacent volumes
9047             UpdateVolumes(n12, n22, nodeList);
9048           }
9049           // 3. find an element appeared on n1 and n2 after the insertion
9050           insertMap.erase( elem );
9051           elem = findAdjacentFace( n1, n2, 0 );
9052         }
9053         if ( notFound || otherLink ) {
9054           // add element and nodes of the side into the insertMap
9055           insertMapIt = insertMap.insert
9056             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9057           (*insertMapIt).second.push_back( n1 );
9058           (*insertMapIt).second.push_back( n2 );
9059         }
9060         // add node to be inserted into elem
9061         (*insertMapIt).second.push_back( nIns );
9062         next[ 1 - intoBord ] = true;
9063       }
9064
9065       // go to the next segment
9066       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9067         if ( next[ iBord ] ) {
9068           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9069             eIt[ iBord ]++;
9070           nPrev[ iBord ] = *nIt[ iBord ];
9071           nIt[ iBord ]++; i[ iBord ]++;
9072         }
9073       }
9074     }
9075     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9076
9077     // perform insertion of nodes into elements
9078
9079     for (insertMapIt = insertMap.begin();
9080          insertMapIt != insertMap.end();
9081          insertMapIt++ )
9082     {
9083       const SMDS_MeshElement* elem = (*insertMapIt).first;
9084       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9085       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9086       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9087
9088       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9089
9090       if ( !theSideIsFreeBorder ) {
9091         // look for and insert nodes into the faces adjacent to elem
9092         while (true) {
9093           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9094           if ( adjElem )
9095             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9096           else
9097             break;
9098         }
9099       }
9100       if (toCreatePolyedrs) {
9101         // perform insertion into the links of adjacent volumes
9102         UpdateVolumes(n1, n2, nodeList);
9103       }
9104     }
9105
9106     delete param[0];
9107     delete param[1];
9108   } // end: insert new nodes
9109
9110   MergeNodes ( nodeGroupsToMerge );
9111
9112   return aResult;
9113 }
9114
9115 //=======================================================================
9116 //function : InsertNodesIntoLink
9117 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9118 //           and theBetweenNode2 and split theElement
9119 //=======================================================================
9120
9121 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9122                                            const SMDS_MeshNode*        theBetweenNode1,
9123                                            const SMDS_MeshNode*        theBetweenNode2,
9124                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9125                                            const bool                  toCreatePoly)
9126 {
9127   if ( theFace->GetType() != SMDSAbs_Face ) return;
9128
9129   // find indices of 2 link nodes and of the rest nodes
9130   int iNode = 0, il1, il2, i3, i4;
9131   il1 = il2 = i3 = i4 = -1;
9132   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9133   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9134
9135   if(theFace->IsQuadratic()) {
9136     const SMDS_VtkFace* F =
9137       dynamic_cast<const SMDS_VtkFace*>(theFace);
9138     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9139     // use special nodes iterator
9140     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9141     while( anIter->more() ) {
9142       const SMDS_MeshNode* n = cast2Node(anIter->next());
9143       if ( n == theBetweenNode1 )
9144         il1 = iNode;
9145       else if ( n == theBetweenNode2 )
9146         il2 = iNode;
9147       else if ( i3 < 0 )
9148         i3 = iNode;
9149       else
9150         i4 = iNode;
9151       nodes[ iNode++ ] = n;
9152     }
9153   }
9154   else {
9155     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9156     while ( nodeIt->more() ) {
9157       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9158       if ( n == theBetweenNode1 )
9159         il1 = iNode;
9160       else if ( n == theBetweenNode2 )
9161         il2 = iNode;
9162       else if ( i3 < 0 )
9163         i3 = iNode;
9164       else
9165         i4 = iNode;
9166       nodes[ iNode++ ] = n;
9167     }
9168   }
9169   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9170     return ;
9171
9172   // arrange link nodes to go one after another regarding the face orientation
9173   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9174   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9175   if ( reverse ) {
9176     iNode = il1;
9177     il1 = il2;
9178     il2 = iNode;
9179     aNodesToInsert.reverse();
9180   }
9181   // check that not link nodes of a quadrangles are in good order
9182   int nbFaceNodes = theFace->NbNodes();
9183   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9184     iNode = i3;
9185     i3 = i4;
9186     i4 = iNode;
9187   }
9188
9189   if (toCreatePoly || theFace->IsPoly()) {
9190
9191     iNode = 0;
9192     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9193
9194     // add nodes of face up to first node of link
9195     bool isFLN = false;
9196
9197     if(theFace->IsQuadratic()) {
9198       const SMDS_VtkFace* F =
9199         dynamic_cast<const SMDS_VtkFace*>(theFace);
9200       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9201       // use special nodes iterator
9202       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9203       while( anIter->more()  && !isFLN ) {
9204         const SMDS_MeshNode* n = cast2Node(anIter->next());
9205         poly_nodes[iNode++] = n;
9206         if (n == nodes[il1]) {
9207           isFLN = true;
9208         }
9209       }
9210       // add nodes to insert
9211       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9212       for (; nIt != aNodesToInsert.end(); nIt++) {
9213         poly_nodes[iNode++] = *nIt;
9214       }
9215       // add nodes of face starting from last node of link
9216       while ( anIter->more() ) {
9217         poly_nodes[iNode++] = cast2Node(anIter->next());
9218       }
9219     }
9220     else {
9221       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9222       while ( nodeIt->more() && !isFLN ) {
9223         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9224         poly_nodes[iNode++] = n;
9225         if (n == nodes[il1]) {
9226           isFLN = true;
9227         }
9228       }
9229       // add nodes to insert
9230       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9231       for (; nIt != aNodesToInsert.end(); nIt++) {
9232         poly_nodes[iNode++] = *nIt;
9233       }
9234       // add nodes of face starting from last node of link
9235       while ( nodeIt->more() ) {
9236         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9237         poly_nodes[iNode++] = n;
9238       }
9239     }
9240
9241     // edit or replace the face
9242     SMESHDS_Mesh *aMesh = GetMeshDS();
9243
9244     if (theFace->IsPoly()) {
9245       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9246     }
9247     else {
9248       int aShapeId = FindShape( theFace );
9249
9250       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9251       myLastCreatedElems.Append(newElem);
9252       if ( aShapeId && newElem )
9253         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9254
9255       aMesh->RemoveElement(theFace);
9256     }
9257     return;
9258   }
9259
9260   SMESHDS_Mesh *aMesh = GetMeshDS();
9261   if( !theFace->IsQuadratic() ) {
9262
9263     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9264     int nbLinkNodes = 2 + aNodesToInsert.size();
9265     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9266     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9267     linkNodes[ 0 ] = nodes[ il1 ];
9268     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9269     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9270     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9271       linkNodes[ iNode++ ] = *nIt;
9272     }
9273     // decide how to split a quadrangle: compare possible variants
9274     // and choose which of splits to be a quadrangle
9275     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9276     if ( nbFaceNodes == 3 ) {
9277       iBestQuad = nbSplits;
9278       i4 = i3;
9279     }
9280     else if ( nbFaceNodes == 4 ) {
9281       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9282       double aBestRate = DBL_MAX;
9283       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9284         i1 = 0; i2 = 1;
9285         double aBadRate = 0;
9286         // evaluate elements quality
9287         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9288           if ( iSplit == iQuad ) {
9289             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9290                                    linkNodes[ i2++ ],
9291                                    nodes[ i3 ],
9292                                    nodes[ i4 ]);
9293             aBadRate += getBadRate( &quad, aCrit );
9294           }
9295           else {
9296             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9297                                    linkNodes[ i2++ ],
9298                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9299             aBadRate += getBadRate( &tria, aCrit );
9300           }
9301         }
9302         // choice
9303         if ( aBadRate < aBestRate ) {
9304           iBestQuad = iQuad;
9305           aBestRate = aBadRate;
9306         }
9307       }
9308     }
9309
9310     // create new elements
9311     int aShapeId = FindShape( theFace );
9312
9313     i1 = 0; i2 = 1;
9314     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9315       SMDS_MeshElement* newElem = 0;
9316       if ( iSplit == iBestQuad )
9317         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9318                                   linkNodes[ i2++ ],
9319                                   nodes[ i3 ],
9320                                   nodes[ i4 ]);
9321       else
9322         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9323                                   linkNodes[ i2++ ],
9324                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9325       myLastCreatedElems.Append(newElem);
9326       if ( aShapeId && newElem )
9327         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9328     }
9329
9330     // change nodes of theFace
9331     const SMDS_MeshNode* newNodes[ 4 ];
9332     newNodes[ 0 ] = linkNodes[ i1 ];
9333     newNodes[ 1 ] = linkNodes[ i2 ];
9334     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9335     newNodes[ 3 ] = nodes[ i4 ];
9336     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9337     const SMDS_MeshElement* newElem = 0;
9338     if (iSplit == iBestQuad)
9339       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9340     else
9341       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9342     myLastCreatedElems.Append(newElem);
9343     if ( aShapeId && newElem )
9344       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9345 } // end if(!theFace->IsQuadratic())
9346   else { // theFace is quadratic
9347     // we have to split theFace on simple triangles and one simple quadrangle
9348     int tmp = il1/2;
9349     int nbshift = tmp*2;
9350     // shift nodes in nodes[] by nbshift
9351     int i,j;
9352     for(i=0; i<nbshift; i++) {
9353       const SMDS_MeshNode* n = nodes[0];
9354       for(j=0; j<nbFaceNodes-1; j++) {
9355         nodes[j] = nodes[j+1];
9356       }
9357       nodes[nbFaceNodes-1] = n;
9358     }
9359     il1 = il1 - nbshift;
9360     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9361     //   n0      n1     n2    n0      n1     n2
9362     //     +-----+-----+        +-----+-----+
9363     //      \         /         |           |
9364     //       \       /          |           |
9365     //      n5+     +n3       n7+           +n3
9366     //         \   /            |           |
9367     //          \ /             |           |
9368     //           +              +-----+-----+
9369     //           n4           n6      n5     n4
9370
9371     // create new elements
9372     int aShapeId = FindShape( theFace );
9373
9374     int n1,n2,n3;
9375     if(nbFaceNodes==6) { // quadratic triangle
9376       SMDS_MeshElement* newElem =
9377         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9378       myLastCreatedElems.Append(newElem);
9379       if ( aShapeId && newElem )
9380         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9381       if(theFace->IsMediumNode(nodes[il1])) {
9382         // create quadrangle
9383         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9384         myLastCreatedElems.Append(newElem);
9385         if ( aShapeId && newElem )
9386           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9387         n1 = 1;
9388         n2 = 2;
9389         n3 = 3;
9390       }
9391       else {
9392         // create quadrangle
9393         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9394         myLastCreatedElems.Append(newElem);
9395         if ( aShapeId && newElem )
9396           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9397         n1 = 0;
9398         n2 = 1;
9399         n3 = 5;
9400       }
9401     }
9402     else { // nbFaceNodes==8 - quadratic quadrangle
9403       SMDS_MeshElement* newElem =
9404         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9405       myLastCreatedElems.Append(newElem);
9406       if ( aShapeId && newElem )
9407         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9408       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9409       myLastCreatedElems.Append(newElem);
9410       if ( aShapeId && newElem )
9411         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9412       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9413       myLastCreatedElems.Append(newElem);
9414       if ( aShapeId && newElem )
9415         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9416       if(theFace->IsMediumNode(nodes[il1])) {
9417         // create quadrangle
9418         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9419         myLastCreatedElems.Append(newElem);
9420         if ( aShapeId && newElem )
9421           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9422         n1 = 1;
9423         n2 = 2;
9424         n3 = 3;
9425       }
9426       else {
9427         // create quadrangle
9428         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9429         myLastCreatedElems.Append(newElem);
9430         if ( aShapeId && newElem )
9431           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9432         n1 = 0;
9433         n2 = 1;
9434         n3 = 7;
9435       }
9436     }
9437     // create needed triangles using n1,n2,n3 and inserted nodes
9438     int nbn = 2 + aNodesToInsert.size();
9439     //const SMDS_MeshNode* aNodes[nbn];
9440     vector<const SMDS_MeshNode*> aNodes(nbn);
9441     aNodes[0] = nodes[n1];
9442     aNodes[nbn-1] = nodes[n2];
9443     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9444     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9445       aNodes[iNode++] = *nIt;
9446     }
9447     for(i=1; i<nbn; i++) {
9448       SMDS_MeshElement* newElem =
9449         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9450       myLastCreatedElems.Append(newElem);
9451       if ( aShapeId && newElem )
9452         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9453     }
9454   }
9455   // remove old face
9456   aMesh->RemoveElement(theFace);
9457 }
9458
9459 //=======================================================================
9460 //function : UpdateVolumes
9461 //purpose  :
9462 //=======================================================================
9463 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9464                                       const SMDS_MeshNode*        theBetweenNode2,
9465                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9466 {
9467   myLastCreatedElems.Clear();
9468   myLastCreatedNodes.Clear();
9469
9470   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9471   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9472     const SMDS_MeshElement* elem = invElemIt->next();
9473
9474     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9475     SMDS_VolumeTool aVolume (elem);
9476     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9477       continue;
9478
9479     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9480     int iface, nbFaces = aVolume.NbFaces();
9481     vector<const SMDS_MeshNode *> poly_nodes;
9482     vector<int> quantities (nbFaces);
9483
9484     for (iface = 0; iface < nbFaces; iface++) {
9485       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9486       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9487       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9488
9489       for (int inode = 0; inode < nbFaceNodes; inode++) {
9490         poly_nodes.push_back(faceNodes[inode]);
9491
9492         if (nbInserted == 0) {
9493           if (faceNodes[inode] == theBetweenNode1) {
9494             if (faceNodes[inode + 1] == theBetweenNode2) {
9495               nbInserted = theNodesToInsert.size();
9496
9497               // add nodes to insert
9498               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9499               for (; nIt != theNodesToInsert.end(); nIt++) {
9500                 poly_nodes.push_back(*nIt);
9501               }
9502             }
9503           }
9504           else if (faceNodes[inode] == theBetweenNode2) {
9505             if (faceNodes[inode + 1] == theBetweenNode1) {
9506               nbInserted = theNodesToInsert.size();
9507
9508               // add nodes to insert in reversed order
9509               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9510               nIt--;
9511               for (; nIt != theNodesToInsert.begin(); nIt--) {
9512                 poly_nodes.push_back(*nIt);
9513               }
9514               poly_nodes.push_back(*nIt);
9515             }
9516           }
9517           else {
9518           }
9519         }
9520       }
9521       quantities[iface] = nbFaceNodes + nbInserted;
9522     }
9523
9524     // Replace or update the volume
9525     SMESHDS_Mesh *aMesh = GetMeshDS();
9526
9527     if (elem->IsPoly()) {
9528       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9529
9530     }
9531     else {
9532       int aShapeId = FindShape( elem );
9533
9534       SMDS_MeshElement* newElem =
9535         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9536       myLastCreatedElems.Append(newElem);
9537       if (aShapeId && newElem)
9538         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9539
9540       aMesh->RemoveElement(elem);
9541     }
9542   }
9543 }
9544
9545 namespace
9546 {
9547   //================================================================================
9548   /*!
9549    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9550    */
9551   //================================================================================
9552
9553   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9554                            vector<const SMDS_MeshNode *> & nodes,
9555                            vector<int> &                   nbNodeInFaces )
9556   {
9557     nodes.clear();
9558     nbNodeInFaces.clear();
9559     SMDS_VolumeTool vTool ( elem );
9560     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9561     {
9562       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9563       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9564       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9565     }
9566   }
9567 }
9568
9569 //=======================================================================
9570 /*!
9571  * \brief Convert elements contained in a submesh to quadratic
9572  * \return int - nb of checked elements
9573  */
9574 //=======================================================================
9575
9576 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9577                                              SMESH_MesherHelper& theHelper,
9578                                              const bool          theForce3d)
9579 {
9580   int nbElem = 0;
9581   if( !theSm ) return nbElem;
9582
9583   vector<int> nbNodeInFaces;
9584   vector<const SMDS_MeshNode *> nodes;
9585   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9586   while(ElemItr->more())
9587   {
9588     nbElem++;
9589     const SMDS_MeshElement* elem = ElemItr->next();
9590     if( !elem ) continue;
9591
9592     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9593     if ( elem->IsQuadratic() )
9594     {
9595       bool alreadyOK;
9596       switch ( aGeomType ) {
9597       case SMDSEntity_Quad_Quadrangle:
9598       case SMDSEntity_Quad_Hexa:         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9599       case SMDSEntity_BiQuad_Quadrangle:
9600       case SMDSEntity_TriQuad_Hexa:      alreadyOK = theHelper.GetIsBiQuadratic(); break;
9601       default:                           alreadyOK = true;
9602       }
9603       if ( alreadyOK ) continue;
9604     }
9605     // get elem data needed to re-create it
9606     //
9607     const int id                     = elem->GetID();
9608     const int nbNodes                = elem->NbCornerNodes();
9609     const SMDSAbs_ElementType aType  = elem->GetType();
9610     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9611     if ( aGeomType == SMDSEntity_Polyhedra )
9612       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9613     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9614       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9615
9616     // remove a linear element
9617     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9618
9619     const SMDS_MeshElement* NewElem = 0;
9620
9621     switch( aType )
9622     {
9623     case SMDSAbs_Edge :
9624       {
9625         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9626         break;
9627       }
9628     case SMDSAbs_Face :
9629       {
9630         switch(nbNodes)
9631         {
9632         case 3:
9633           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9634           break;
9635         case 4:
9636           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9637           break;
9638         default:
9639           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9640           continue;
9641         }
9642         break;
9643       }
9644     case SMDSAbs_Volume :
9645       {
9646         switch( aGeomType )
9647         {
9648         case SMDSEntity_Tetra:
9649           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9650           break;
9651         case SMDSEntity_Pyramid:
9652           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9653           break;
9654         case SMDSEntity_Penta:
9655           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9656           break;
9657         case SMDSEntity_Hexa:
9658         case SMDSEntity_Quad_Hexa:
9659         case SMDSEntity_TriQuad_Hexa:
9660           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9661                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9662           break;
9663         case SMDSEntity_Hexagonal_Prism:
9664         default:
9665           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9666         }
9667         break;
9668       }
9669     default :
9670       continue;
9671     }
9672     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9673     if( NewElem )
9674       theSm->AddElement( NewElem );
9675   }
9676   return nbElem;
9677 }
9678 //=======================================================================
9679 //function : ConvertToQuadratic
9680 //purpose  :
9681 //=======================================================================
9682
9683 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9684 {
9685   SMESHDS_Mesh* meshDS = GetMeshDS();
9686
9687   SMESH_MesherHelper aHelper(*myMesh);
9688
9689   aHelper.SetIsQuadratic( true );
9690   aHelper.SetIsBiQuadratic( theToBiQuad );
9691   aHelper.SetElementsOnShape(true);
9692
9693   int nbCheckedElems = 0;
9694   if ( myMesh->HasShapeToMesh() )
9695   {
9696     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9697     {
9698       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9699       while ( smIt->more() ) {
9700         SMESH_subMesh* sm = smIt->next();
9701         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9702           aHelper.SetSubShape( sm->GetSubShape() );
9703           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9704         }
9705       }
9706     }
9707   }
9708   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9709   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9710   {
9711     SMESHDS_SubMesh *smDS = 0;
9712     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9713     while(aEdgeItr->more())
9714     {
9715       const SMDS_MeshEdge* edge = aEdgeItr->next();
9716       if(edge && !edge->IsQuadratic())
9717       {
9718         int id = edge->GetID();
9719         //MESSAGE("edge->GetID() " << id);
9720         const SMDS_MeshNode* n1 = edge->GetNode(0);
9721         const SMDS_MeshNode* n2 = edge->GetNode(1);
9722
9723         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9724
9725         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9726         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9727       }
9728     }
9729     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9730     while(aFaceItr->more())
9731     {
9732       const SMDS_MeshFace* face = aFaceItr->next();
9733       if ( !face ) continue;
9734       
9735       const SMDSAbs_EntityType type = face->GetEntityType();
9736       if (( theToBiQuad  && type == SMDSEntity_BiQuad_Quadrangle ) ||
9737           ( !theToBiQuad && type == SMDSEntity_Quad_Quadrangle ))
9738         continue;
9739
9740       const int id = face->GetID();
9741       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9742
9743       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9744
9745       SMDS_MeshFace * NewFace = 0;
9746       switch( type )
9747       {
9748       case SMDSEntity_Triangle:
9749         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9750         break;
9751       case SMDSEntity_Quadrangle:
9752         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9753         break;
9754       default:
9755         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9756       }
9757       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9758     }
9759     vector<int> nbNodeInFaces;
9760     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9761     while(aVolumeItr->more())
9762     {
9763       const SMDS_MeshVolume* volume = aVolumeItr->next();
9764       if(!volume || volume->IsQuadratic() ) continue;
9765
9766       const SMDSAbs_EntityType type = volume->GetEntityType();
9767       if (( theToBiQuad  && type == SMDSEntity_TriQuad_Hexa ) ||
9768           ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
9769         continue;
9770
9771       const int id = volume->GetID();
9772       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9773       if ( type == SMDSEntity_Polyhedra )
9774         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9775       else if ( type == SMDSEntity_Hexagonal_Prism )
9776         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9777
9778       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9779
9780       SMDS_MeshVolume * NewVolume = 0;
9781       switch ( type )
9782       {
9783       case SMDSEntity_Tetra:
9784         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9785         break;
9786       case SMDSEntity_Hexa:
9787       case SMDSEntity_Quad_Hexa:
9788       case SMDSEntity_TriQuad_Hexa:
9789         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9790                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9791         break;
9792       case SMDSEntity_Pyramid:
9793         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9794                                       nodes[3], nodes[4], id, theForce3d);
9795         break;
9796       case SMDSEntity_Penta:
9797         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9798                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9799         break;
9800       case SMDSEntity_Hexagonal_Prism:
9801       default:
9802         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9803       }
9804       ReplaceElemInGroups(volume, NewVolume, meshDS);
9805     }
9806   }
9807
9808   if ( !theForce3d )
9809   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9810     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9811     aHelper.FixQuadraticElements(myError);
9812   }
9813 }
9814
9815 //================================================================================
9816 /*!
9817  * \brief Makes given elements quadratic
9818  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9819  *  \param theElements - elements to make quadratic
9820  */
9821 //================================================================================
9822
9823 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9824                                           TIDSortedElemSet& theElements,
9825                                           const bool        theToBiQuad)
9826 {
9827   if ( theElements.empty() ) return;
9828
9829   // we believe that all theElements are of the same type
9830   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9831
9832   // get all nodes shared by theElements
9833   TIDSortedNodeSet allNodes;
9834   TIDSortedElemSet::iterator eIt = theElements.begin();
9835   for ( ; eIt != theElements.end(); ++eIt )
9836     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9837
9838   // complete theElements with elements of lower dim whose all nodes are in allNodes
9839
9840   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9841   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9842   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9843   for ( ; nIt != allNodes.end(); ++nIt )
9844   {
9845     const SMDS_MeshNode* n = *nIt;
9846     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9847     while ( invIt->more() )
9848     {
9849       const SMDS_MeshElement* e = invIt->next();
9850       if ( e->IsQuadratic() )
9851       {
9852         bool alreadyOK;
9853         switch ( e->GetEntityType() ) {
9854         case SMDSEntity_Quad_Quadrangle:
9855         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9856         case SMDSEntity_BiQuad_Quadrangle:
9857         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9858         default:                           alreadyOK = true;
9859         }
9860         if ( alreadyOK )
9861         {
9862           quadAdjacentElems[ e->GetType() ].insert( e );
9863           continue;
9864         }
9865       }
9866       if ( e->GetType() >= elemType )
9867       {
9868         continue; // same type of more complex linear element
9869       }
9870
9871       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9872         continue; // e is already checked
9873
9874       // check nodes
9875       bool allIn = true;
9876       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9877       while ( nodeIt->more() && allIn )
9878         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9879       if ( allIn )
9880         theElements.insert(e );
9881     }
9882   }
9883
9884   SMESH_MesherHelper helper(*myMesh);
9885   helper.SetIsQuadratic( true );
9886   helper.SetIsBiQuadratic( theToBiQuad );
9887
9888   // add links of quadratic adjacent elements to the helper
9889
9890   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9891     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9892           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9893     {
9894       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9895     }
9896   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9897     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9898           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9899     {
9900       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9901     }
9902   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9903     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9904           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9905     {
9906       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9907     }
9908
9909   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9910
9911   SMESHDS_Mesh*  meshDS = GetMeshDS();
9912   SMESHDS_SubMesh* smDS = 0;
9913   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9914   {
9915     const SMDS_MeshElement* elem = *eIt;
9916     if( elem->NbNodes() < 2 || elem->IsPoly() )
9917       continue;
9918
9919     if ( elem->IsQuadratic() )
9920     {
9921       bool alreadyOK;
9922       switch ( elem->GetEntityType() ) {
9923       case SMDSEntity_Quad_Quadrangle:
9924       case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9925       case SMDSEntity_BiQuad_Quadrangle:
9926       case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9927       default:                           alreadyOK = true;
9928       }
9929       if ( alreadyOK ) continue;
9930     }
9931
9932     const SMDSAbs_ElementType type = elem->GetType();
9933     const int                   id = elem->GetID();
9934     const int              nbNodes = elem->NbCornerNodes();
9935     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9936
9937     if ( !smDS || !smDS->Contains( elem ))
9938       smDS = meshDS->MeshElements( elem->getshapeId() );
9939     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9940
9941     SMDS_MeshElement * newElem = 0;
9942     switch( nbNodes )
9943     {
9944     case 4: // cases for most frequently used element types go first (for optimization)
9945       if ( type == SMDSAbs_Volume )
9946         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9947       else
9948         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9949       break;
9950     case 8:
9951       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9952                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9953       break;
9954     case 3:
9955       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9956       break;
9957     case 2:
9958       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9959       break;
9960     case 5:
9961       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9962                                  nodes[4], id, theForce3d);
9963       break;
9964     case 6:
9965       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9966                                  nodes[4], nodes[5], id, theForce3d);
9967       break;
9968     default:;
9969     }
9970     ReplaceElemInGroups( elem, newElem, meshDS);
9971     if( newElem && smDS )
9972       smDS->AddElement( newElem );
9973   }
9974
9975   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9976   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9977     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9978     helper.FixQuadraticElements( myError );
9979   }
9980 }
9981
9982 //=======================================================================
9983 /*!
9984  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9985  * \return int - nb of checked elements
9986  */
9987 //=======================================================================
9988
9989 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9990                                      SMDS_ElemIteratorPtr theItr,
9991                                      const int            theShapeID)
9992 {
9993   int nbElem = 0;
9994   SMESHDS_Mesh* meshDS = GetMeshDS();
9995
9996   while( theItr->more() )
9997   {
9998     const SMDS_MeshElement* elem = theItr->next();
9999     nbElem++;
10000     if( elem && elem->IsQuadratic())
10001     {
10002       int id                    = elem->GetID();
10003       int nbCornerNodes         = elem->NbCornerNodes();
10004       SMDSAbs_ElementType aType = elem->GetType();
10005
10006       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
10007
10008       //remove a quadratic element
10009       if ( !theSm || !theSm->Contains( elem ))
10010         theSm = meshDS->MeshElements( elem->getshapeId() );
10011       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
10012
10013       // remove medium nodes
10014       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
10015         if ( nodes[i]->NbInverseElements() == 0 )
10016           meshDS->RemoveFreeNode( nodes[i], theSm );
10017
10018       // add a linear element
10019       nodes.resize( nbCornerNodes );
10020       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
10021       ReplaceElemInGroups(elem, newElem, meshDS);
10022       if( theSm && newElem )
10023         theSm->AddElement( newElem );
10024     }
10025   }
10026   return nbElem;
10027 }
10028
10029 //=======================================================================
10030 //function : ConvertFromQuadratic
10031 //purpose  :
10032 //=======================================================================
10033
10034 bool SMESH_MeshEditor::ConvertFromQuadratic()
10035 {
10036   int nbCheckedElems = 0;
10037   if ( myMesh->HasShapeToMesh() )
10038   {
10039     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
10040     {
10041       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
10042       while ( smIt->more() ) {
10043         SMESH_subMesh* sm = smIt->next();
10044         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
10045           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
10046       }
10047     }
10048   }
10049
10050   int totalNbElems =
10051     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
10052   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10053   {
10054     SMESHDS_SubMesh *aSM = 0;
10055     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10056   }
10057
10058   return true;
10059 }
10060
10061 namespace
10062 {
10063   //================================================================================
10064   /*!
10065    * \brief Return true if all medium nodes of the element are in the node set
10066    */
10067   //================================================================================
10068
10069   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10070   {
10071     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10072       if ( !nodeSet.count( elem->GetNode(i) ))
10073         return false;
10074     return true;
10075   }
10076 }
10077
10078 //================================================================================
10079 /*!
10080  * \brief Makes given elements linear
10081  */
10082 //================================================================================
10083
10084 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10085 {
10086   if ( theElements.empty() ) return;
10087
10088   // collect IDs of medium nodes of theElements; some of these nodes will be removed
10089   set<int> mediumNodeIDs;
10090   TIDSortedElemSet::iterator eIt = theElements.begin();
10091   for ( ; eIt != theElements.end(); ++eIt )
10092   {
10093     const SMDS_MeshElement* e = *eIt;
10094     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10095       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10096   }
10097
10098   // replace given elements by linear ones
10099   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10100   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10101   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10102
10103   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10104   // except those elements sharing medium nodes of quadratic element whose medium nodes
10105   // are not all in mediumNodeIDs
10106
10107   // get remaining medium nodes
10108   TIDSortedNodeSet mediumNodes;
10109   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10110   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10111     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10112       mediumNodes.insert( mediumNodes.end(), n );
10113
10114   // find more quadratic elements to convert
10115   TIDSortedElemSet moreElemsToConvert;
10116   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10117   for ( ; nIt != mediumNodes.end(); ++nIt )
10118   {
10119     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10120     while ( invIt->more() )
10121     {
10122       const SMDS_MeshElement* e = invIt->next();
10123       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10124       {
10125         // find a more complex element including e and
10126         // whose medium nodes are not in mediumNodes
10127         bool complexFound = false;
10128         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10129         {
10130           SMDS_ElemIteratorPtr invIt2 =
10131             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10132           while ( invIt2->more() )
10133           {
10134             const SMDS_MeshElement* eComplex = invIt2->next();
10135             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10136             {
10137               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10138               if ( nbCommonNodes == e->NbNodes())
10139               {
10140                 complexFound = true;
10141                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10142                 break;
10143               }
10144             }
10145           }
10146         }
10147         if ( !complexFound )
10148           moreElemsToConvert.insert( e );
10149       }
10150     }
10151   }
10152   elemIt = SMDS_ElemIteratorPtr
10153     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10154   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10155 }
10156
10157 //=======================================================================
10158 //function : SewSideElements
10159 //purpose  :
10160 //=======================================================================
10161
10162 SMESH_MeshEditor::Sew_Error
10163 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10164                                    TIDSortedElemSet&    theSide2,
10165                                    const SMDS_MeshNode* theFirstNode1,
10166                                    const SMDS_MeshNode* theFirstNode2,
10167                                    const SMDS_MeshNode* theSecondNode1,
10168                                    const SMDS_MeshNode* theSecondNode2)
10169 {
10170   myLastCreatedElems.Clear();
10171   myLastCreatedNodes.Clear();
10172
10173   MESSAGE ("::::SewSideElements()");
10174   if ( theSide1.size() != theSide2.size() )
10175     return SEW_DIFF_NB_OF_ELEMENTS;
10176
10177   Sew_Error aResult = SEW_OK;
10178   // Algo:
10179   // 1. Build set of faces representing each side
10180   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10181   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10182
10183   // =======================================================================
10184   // 1. Build set of faces representing each side:
10185   // =======================================================================
10186   // a. build set of nodes belonging to faces
10187   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10188   // c. create temporary faces representing side of volumes if correspondent
10189   //    face does not exist
10190
10191   SMESHDS_Mesh* aMesh = GetMeshDS();
10192   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10193   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10194   TIDSortedElemSet             faceSet1, faceSet2;
10195   set<const SMDS_MeshElement*> volSet1,  volSet2;
10196   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10197   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10198   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10199   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10200   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10201   int iSide, iFace, iNode;
10202
10203   list<const SMDS_MeshElement* > tempFaceList;
10204   for ( iSide = 0; iSide < 2; iSide++ ) {
10205     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10206     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10207     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10208     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10209     set<const SMDS_MeshElement*>::iterator vIt;
10210     TIDSortedElemSet::iterator eIt;
10211     set<const SMDS_MeshNode*>::iterator    nIt;
10212
10213     // check that given nodes belong to given elements
10214     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10215     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10216     int firstIndex = -1, secondIndex = -1;
10217     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10218       const SMDS_MeshElement* elem = *eIt;
10219       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10220       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10221       if ( firstIndex > -1 && secondIndex > -1 ) break;
10222     }
10223     if ( firstIndex < 0 || secondIndex < 0 ) {
10224       // we can simply return until temporary faces created
10225       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10226     }
10227
10228     // -----------------------------------------------------------
10229     // 1a. Collect nodes of existing faces
10230     //     and build set of face nodes in order to detect missing
10231     //     faces corresponding to sides of volumes
10232     // -----------------------------------------------------------
10233
10234     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10235
10236     // loop on the given element of a side
10237     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10238       //const SMDS_MeshElement* elem = *eIt;
10239       const SMDS_MeshElement* elem = *eIt;
10240       if ( elem->GetType() == SMDSAbs_Face ) {
10241         faceSet->insert( elem );
10242         set <const SMDS_MeshNode*> faceNodeSet;
10243         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10244         while ( nodeIt->more() ) {
10245           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10246           nodeSet->insert( n );
10247           faceNodeSet.insert( n );
10248         }
10249         setOfFaceNodeSet.insert( faceNodeSet );
10250       }
10251       else if ( elem->GetType() == SMDSAbs_Volume )
10252         volSet->insert( elem );
10253     }
10254     // ------------------------------------------------------------------------------
10255     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10256     // ------------------------------------------------------------------------------
10257
10258     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10259       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10260       while ( fIt->more() ) { // loop on faces sharing a node
10261         const SMDS_MeshElement* f = fIt->next();
10262         if ( faceSet->find( f ) == faceSet->end() ) {
10263           // check if all nodes are in nodeSet and
10264           // complete setOfFaceNodeSet if they are
10265           set <const SMDS_MeshNode*> faceNodeSet;
10266           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10267           bool allInSet = true;
10268           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10269             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10270             if ( nodeSet->find( n ) == nodeSet->end() )
10271               allInSet = false;
10272             else
10273               faceNodeSet.insert( n );
10274           }
10275           if ( allInSet ) {
10276             faceSet->insert( f );
10277             setOfFaceNodeSet.insert( faceNodeSet );
10278           }
10279         }
10280       }
10281     }
10282
10283     // -------------------------------------------------------------------------
10284     // 1c. Create temporary faces representing sides of volumes if correspondent
10285     //     face does not exist
10286     // -------------------------------------------------------------------------
10287
10288     if ( !volSet->empty() ) {
10289       //int nodeSetSize = nodeSet->size();
10290
10291       // loop on given volumes
10292       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10293         SMDS_VolumeTool vol (*vIt);
10294         // loop on volume faces: find free faces
10295         // --------------------------------------
10296         list<const SMDS_MeshElement* > freeFaceList;
10297         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10298           if ( !vol.IsFreeFace( iFace ))
10299             continue;
10300           // check if there is already a face with same nodes in a face set
10301           const SMDS_MeshElement* aFreeFace = 0;
10302           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10303           int nbNodes = vol.NbFaceNodes( iFace );
10304           set <const SMDS_MeshNode*> faceNodeSet;
10305           vol.GetFaceNodes( iFace, faceNodeSet );
10306           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10307           if ( isNewFace ) {
10308             // no such a face is given but it still can exist, check it
10309             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10310             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10311           }
10312           if ( !aFreeFace ) {
10313             // create a temporary face
10314             if ( nbNodes == 3 ) {
10315               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10316               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10317             }
10318             else if ( nbNodes == 4 ) {
10319               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10320               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10321             }
10322             else {
10323               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10324               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10325               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10326             }
10327             if ( aFreeFace )
10328               tempFaceList.push_back( aFreeFace );
10329           }
10330
10331           if ( aFreeFace )
10332             freeFaceList.push_back( aFreeFace );
10333
10334         } // loop on faces of a volume
10335
10336         // choose one of several free faces of a volume
10337         // --------------------------------------------
10338         if ( freeFaceList.size() > 1 ) {
10339           // choose a face having max nb of nodes shared by other elems of a side
10340           int maxNbNodes = -1;
10341           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10342           while ( fIt != freeFaceList.end() ) { // loop on free faces
10343             int nbSharedNodes = 0;
10344             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10345             while ( nodeIt->more() ) { // loop on free face nodes
10346               const SMDS_MeshNode* n =
10347                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10348               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10349               while ( invElemIt->more() ) {
10350                 const SMDS_MeshElement* e = invElemIt->next();
10351                 nbSharedNodes += faceSet->count( e );
10352                 nbSharedNodes += elemSet->count( e );
10353               }
10354             }
10355             if ( nbSharedNodes > maxNbNodes ) {
10356               maxNbNodes = nbSharedNodes;
10357               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10358             }
10359             else if ( nbSharedNodes == maxNbNodes ) {
10360               fIt++;
10361             }
10362             else {
10363               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10364             }
10365           }
10366           if ( freeFaceList.size() > 1 )
10367           {
10368             // could not choose one face, use another way
10369             // choose a face most close to the bary center of the opposite side
10370             gp_XYZ aBC( 0., 0., 0. );
10371             set <const SMDS_MeshNode*> addedNodes;
10372             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10373             eIt = elemSet2->begin();
10374             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10375               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10376               while ( nodeIt->more() ) { // loop on free face nodes
10377                 const SMDS_MeshNode* n =
10378                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10379                 if ( addedNodes.insert( n ).second )
10380                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10381               }
10382             }
10383             aBC /= addedNodes.size();
10384             double minDist = DBL_MAX;
10385             fIt = freeFaceList.begin();
10386             while ( fIt != freeFaceList.end() ) { // loop on free faces
10387               double dist = 0;
10388               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10389               while ( nodeIt->more() ) { // loop on free face nodes
10390                 const SMDS_MeshNode* n =
10391                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10392                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10393                 dist += ( aBC - p ).SquareModulus();
10394               }
10395               if ( dist < minDist ) {
10396                 minDist = dist;
10397                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10398               }
10399               else
10400                 fIt = freeFaceList.erase( fIt++ );
10401             }
10402           }
10403         } // choose one of several free faces of a volume
10404
10405         if ( freeFaceList.size() == 1 ) {
10406           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10407           faceSet->insert( aFreeFace );
10408           // complete a node set with nodes of a found free face
10409           //           for ( iNode = 0; iNode < ; iNode++ )
10410           //             nodeSet->insert( fNodes[ iNode ] );
10411         }
10412
10413       } // loop on volumes of a side
10414
10415       //       // complete a set of faces if new nodes in a nodeSet appeared
10416       //       // ----------------------------------------------------------
10417       //       if ( nodeSetSize != nodeSet->size() ) {
10418       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10419       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10420       //           while ( fIt->more() ) { // loop on faces sharing a node
10421       //             const SMDS_MeshElement* f = fIt->next();
10422       //             if ( faceSet->find( f ) == faceSet->end() ) {
10423       //               // check if all nodes are in nodeSet and
10424       //               // complete setOfFaceNodeSet if they are
10425       //               set <const SMDS_MeshNode*> faceNodeSet;
10426       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10427       //               bool allInSet = true;
10428       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10429       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10430       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10431       //                   allInSet = false;
10432       //                 else
10433       //                   faceNodeSet.insert( n );
10434       //               }
10435       //               if ( allInSet ) {
10436       //                 faceSet->insert( f );
10437       //                 setOfFaceNodeSet.insert( faceNodeSet );
10438       //               }
10439       //             }
10440       //           }
10441       //         }
10442       //       }
10443     } // Create temporary faces, if there are volumes given
10444   } // loop on sides
10445
10446   if ( faceSet1.size() != faceSet2.size() ) {
10447     // delete temporary faces: they are in reverseElements of actual nodes
10448 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10449 //    while ( tmpFaceIt->more() )
10450 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10451 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10452 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10453 //      aMesh->RemoveElement(*tmpFaceIt);
10454     MESSAGE("Diff nb of faces");
10455     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10456   }
10457
10458   // ============================================================
10459   // 2. Find nodes to merge:
10460   //              bind a node to remove to a node to put instead
10461   // ============================================================
10462
10463   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10464   if ( theFirstNode1 != theFirstNode2 )
10465     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10466   if ( theSecondNode1 != theSecondNode2 )
10467     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10468
10469   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10470   set< long > linkIdSet; // links to process
10471   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10472
10473   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10474   list< NLink > linkList[2];
10475   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10476   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10477   // loop on links in linkList; find faces by links and append links
10478   // of the found faces to linkList
10479   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10480   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10481   {
10482     NLink link[] = { *linkIt[0], *linkIt[1] };
10483     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10484     if ( !linkIdSet.count( linkID ) )
10485       continue;
10486
10487     // by links, find faces in the face sets,
10488     // and find indices of link nodes in the found faces;
10489     // in a face set, there is only one or no face sharing a link
10490     // ---------------------------------------------------------------
10491
10492     const SMDS_MeshElement* face[] = { 0, 0 };
10493     vector<const SMDS_MeshNode*> fnodes[2];
10494     int iLinkNode[2][2];
10495     TIDSortedElemSet avoidSet;
10496     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10497       const SMDS_MeshNode* n1 = link[iSide].first;
10498       const SMDS_MeshNode* n2 = link[iSide].second;
10499       //cout << "Side " << iSide << " ";
10500       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10501       // find a face by two link nodes
10502       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10503                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10504       if ( face[ iSide ])
10505       {
10506         //cout << " F " << face[ iSide]->GetID() <<endl;
10507         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10508         // put face nodes to fnodes
10509         if ( face[ iSide ]->IsQuadratic() )
10510         {
10511           // use interlaced nodes iterator
10512           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10513           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10514           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10515           while ( nIter->more() )
10516             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10517         }
10518         else
10519         {
10520           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10521                                   face[ iSide ]->end_nodes() );
10522         }
10523         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10524       }
10525     }
10526
10527     // check similarity of elements of the sides
10528     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10529       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10530       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10531         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10532       }
10533       else {
10534         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10535       }
10536       break; // do not return because it's necessary to remove tmp faces
10537     }
10538
10539     // set nodes to merge
10540     // -------------------
10541
10542     if ( face[0] && face[1] )  {
10543       const int nbNodes = face[0]->NbNodes();
10544       if ( nbNodes != face[1]->NbNodes() ) {
10545         MESSAGE("Diff nb of face nodes");
10546         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10547         break; // do not return because it s necessary to remove tmp faces
10548       }
10549       bool reverse[] = { false, false }; // order of nodes in the link
10550       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10551         // analyse link orientation in faces
10552         int i1 = iLinkNode[ iSide ][ 0 ];
10553         int i2 = iLinkNode[ iSide ][ 1 ];
10554         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10555       }
10556       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10557       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10558       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10559       {
10560         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10561                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10562       }
10563
10564       // add other links of the faces to linkList
10565       // -----------------------------------------
10566
10567       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10568         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10569         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10570         if ( !iter_isnew.second ) { // already in a set: no need to process
10571           linkIdSet.erase( iter_isnew.first );
10572         }
10573         else // new in set == encountered for the first time: add
10574         {
10575           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10576           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10577           linkList[0].push_back ( NLink( n1, n2 ));
10578           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10579         }
10580       }
10581     } // 2 faces found
10582
10583     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10584       break;
10585
10586   } // loop on link lists
10587
10588   if ( aResult == SEW_OK &&
10589        ( //linkIt[0] != linkList[0].end() ||
10590          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10591     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10592              " " << (faceSetPtr[1]->empty()));
10593     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10594   }
10595
10596   // ====================================================================
10597   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10598   // ====================================================================
10599
10600   // delete temporary faces
10601 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10602 //  while ( tmpFaceIt->more() )
10603 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10604   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10605   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10606     aMesh->RemoveElement(*tmpFaceIt);
10607
10608   if ( aResult != SEW_OK)
10609     return aResult;
10610
10611   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10612   // loop on nodes replacement map
10613   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10614   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10615     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10616       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10617       nodeIDsToRemove.push_back( nToRemove->GetID() );
10618       // loop on elements sharing nToRemove
10619       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10620       while ( invElemIt->more() ) {
10621         const SMDS_MeshElement* e = invElemIt->next();
10622         // get a new suite of nodes: make replacement
10623         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10624         vector< const SMDS_MeshNode*> nodes( nbNodes );
10625         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10626         while ( nIt->more() ) {
10627           const SMDS_MeshNode* n =
10628             static_cast<const SMDS_MeshNode*>( nIt->next() );
10629           nnIt = nReplaceMap.find( n );
10630           if ( nnIt != nReplaceMap.end() ) {
10631             nbReplaced++;
10632             n = (*nnIt).second;
10633           }
10634           nodes[ i++ ] = n;
10635         }
10636         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10637         //         elemIDsToRemove.push_back( e->GetID() );
10638         //       else
10639         if ( nbReplaced )
10640           {
10641             SMDSAbs_ElementType etyp = e->GetType();
10642             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10643             if (newElem)
10644               {
10645                 myLastCreatedElems.Append(newElem);
10646                 AddToSameGroups(newElem, e, aMesh);
10647                 int aShapeId = e->getshapeId();
10648                 if ( aShapeId )
10649                   {
10650                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10651                   }
10652               }
10653             aMesh->RemoveElement(e);
10654           }
10655       }
10656     }
10657
10658   Remove( nodeIDsToRemove, true );
10659
10660   return aResult;
10661 }
10662
10663 //================================================================================
10664 /*!
10665  * \brief Find corresponding nodes in two sets of faces
10666  * \param theSide1 - first face set
10667  * \param theSide2 - second first face
10668  * \param theFirstNode1 - a boundary node of set 1
10669  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10670  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10671  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10672  * \param nReplaceMap - output map of corresponding nodes
10673  * \return bool  - is a success or not
10674  */
10675 //================================================================================
10676
10677 #ifdef _DEBUG_
10678 //#define DEBUG_MATCHING_NODES
10679 #endif
10680
10681 SMESH_MeshEditor::Sew_Error
10682 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10683                                     set<const SMDS_MeshElement*>& theSide2,
10684                                     const SMDS_MeshNode*          theFirstNode1,
10685                                     const SMDS_MeshNode*          theFirstNode2,
10686                                     const SMDS_MeshNode*          theSecondNode1,
10687                                     const SMDS_MeshNode*          theSecondNode2,
10688                                     TNodeNodeMap &                nReplaceMap)
10689 {
10690   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10691
10692   nReplaceMap.clear();
10693   if ( theFirstNode1 != theFirstNode2 )
10694     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10695   if ( theSecondNode1 != theSecondNode2 )
10696     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10697
10698   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10699   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10700
10701   list< NLink > linkList[2];
10702   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10703   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10704
10705   // loop on links in linkList; find faces by links and append links
10706   // of the found faces to linkList
10707   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10708   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10709     NLink link[] = { *linkIt[0], *linkIt[1] };
10710     if ( linkSet.find( link[0] ) == linkSet.end() )
10711       continue;
10712
10713     // by links, find faces in the face sets,
10714     // and find indices of link nodes in the found faces;
10715     // in a face set, there is only one or no face sharing a link
10716     // ---------------------------------------------------------------
10717
10718     const SMDS_MeshElement* face[] = { 0, 0 };
10719     list<const SMDS_MeshNode*> notLinkNodes[2];
10720     //bool reverse[] = { false, false }; // order of notLinkNodes
10721     int nbNodes[2];
10722     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10723     {
10724       const SMDS_MeshNode* n1 = link[iSide].first;
10725       const SMDS_MeshNode* n2 = link[iSide].second;
10726       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10727       set< const SMDS_MeshElement* > facesOfNode1;
10728       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10729       {
10730         // during a loop of the first node, we find all faces around n1,
10731         // during a loop of the second node, we find one face sharing both n1 and n2
10732         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10733         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10734         while ( fIt->more() ) { // loop on faces sharing a node
10735           const SMDS_MeshElement* f = fIt->next();
10736           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10737               ! facesOfNode1.insert( f ).second ) // f encounters twice
10738           {
10739             if ( face[ iSide ] ) {
10740               MESSAGE( "2 faces per link " );
10741               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10742             }
10743             face[ iSide ] = f;
10744             faceSet->erase( f );
10745
10746             // get not link nodes
10747             int nbN = f->NbNodes();
10748             if ( f->IsQuadratic() )
10749               nbN /= 2;
10750             nbNodes[ iSide ] = nbN;
10751             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10752             int i1 = f->GetNodeIndex( n1 );
10753             int i2 = f->GetNodeIndex( n2 );
10754             int iEnd = nbN, iBeg = -1, iDelta = 1;
10755             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10756             if ( reverse ) {
10757               std::swap( iEnd, iBeg ); iDelta = -1;
10758             }
10759             int i = i2;
10760             while ( true ) {
10761               i += iDelta;
10762               if ( i == iEnd ) i = iBeg + iDelta;
10763               if ( i == i1 ) break;
10764               nodes.push_back ( f->GetNode( i ) );
10765             }
10766           }
10767         }
10768       }
10769     }
10770     // check similarity of elements of the sides
10771     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10772       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10773       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10774         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10775       }
10776       else {
10777         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10778       }
10779     }
10780
10781     // set nodes to merge
10782     // -------------------
10783
10784     if ( face[0] && face[1] )  {
10785       if ( nbNodes[0] != nbNodes[1] ) {
10786         MESSAGE("Diff nb of face nodes");
10787         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10788       }
10789 #ifdef DEBUG_MATCHING_NODES
10790       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10791                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10792                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10793 #endif
10794       int nbN = nbNodes[0];
10795       {
10796         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10797         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10798         for ( int i = 0 ; i < nbN - 2; ++i ) {
10799 #ifdef DEBUG_MATCHING_NODES
10800           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10801 #endif
10802           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10803         }
10804       }
10805
10806       // add other links of the face 1 to linkList
10807       // -----------------------------------------
10808
10809       const SMDS_MeshElement* f0 = face[0];
10810       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10811       for ( int i = 0; i < nbN; i++ )
10812       {
10813         const SMDS_MeshNode* n2 = f0->GetNode( i );
10814         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10815           linkSet.insert( SMESH_TLink( n1, n2 ));
10816         if ( !iter_isnew.second ) { // already in a set: no need to process
10817           linkSet.erase( iter_isnew.first );
10818         }
10819         else // new in set == encountered for the first time: add
10820         {
10821 #ifdef DEBUG_MATCHING_NODES
10822           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10823                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10824 #endif
10825           linkList[0].push_back ( NLink( n1, n2 ));
10826           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10827         }
10828         n1 = n2;
10829       }
10830     } // 2 faces found
10831   } // loop on link lists
10832
10833   return SEW_OK;
10834 }
10835
10836 //================================================================================
10837 /*!
10838   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10839   \param theElems - the list of elements (edges or faces) to be replicated
10840   The nodes for duplication could be found from these elements
10841   \param theNodesNot - list of nodes to NOT replicate
10842   \param theAffectedElems - the list of elements (cells and edges) to which the
10843   replicated nodes should be associated to.
10844   \return TRUE if operation has been completed successfully, FALSE otherwise
10845 */
10846 //================================================================================
10847
10848 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10849                                     const TIDSortedElemSet& theNodesNot,
10850                                     const TIDSortedElemSet& theAffectedElems )
10851 {
10852   myLastCreatedElems.Clear();
10853   myLastCreatedNodes.Clear();
10854
10855   if ( theElems.size() == 0 )
10856     return false;
10857
10858   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10859   if ( !aMeshDS )
10860     return false;
10861
10862   bool res = false;
10863   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10864   // duplicate elements and nodes
10865   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10866   // replce nodes by duplications
10867   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10868   return res;
10869 }
10870
10871 //================================================================================
10872 /*!
10873   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10874   \param theMeshDS - mesh instance
10875   \param theElems - the elements replicated or modified (nodes should be changed)
10876   \param theNodesNot - nodes to NOT replicate
10877   \param theNodeNodeMap - relation of old node to new created node
10878   \param theIsDoubleElem - flag os to replicate element or modify
10879   \return TRUE if operation has been completed successfully, FALSE otherwise
10880 */
10881 //================================================================================
10882
10883 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10884                                     const TIDSortedElemSet& theElems,
10885                                     const TIDSortedElemSet& theNodesNot,
10886                                     std::map< const SMDS_MeshNode*,
10887                                     const SMDS_MeshNode* >& theNodeNodeMap,
10888                                     const bool theIsDoubleElem )
10889 {
10890   MESSAGE("doubleNodes");
10891   // iterate on through element and duplicate them (by nodes duplication)
10892   bool res = false;
10893   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10894   for ( ;  elemItr != theElems.end(); ++elemItr )
10895   {
10896     const SMDS_MeshElement* anElem = *elemItr;
10897     if (!anElem)
10898       continue;
10899
10900     bool isDuplicate = false;
10901     // duplicate nodes to duplicate element
10902     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10903     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10904     int ind = 0;
10905     while ( anIter->more() )
10906     {
10907
10908       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10909       SMDS_MeshNode* aNewNode = aCurrNode;
10910       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10911         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10912       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10913       {
10914         // duplicate node
10915         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10916         theNodeNodeMap[ aCurrNode ] = aNewNode;
10917         myLastCreatedNodes.Append( aNewNode );
10918       }
10919       isDuplicate |= (aCurrNode != aNewNode);
10920       newNodes[ ind++ ] = aNewNode;
10921     }
10922     if ( !isDuplicate )
10923       continue;
10924
10925     if ( theIsDoubleElem )
10926       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10927     else
10928       {
10929       MESSAGE("ChangeElementNodes");
10930       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10931       }
10932     res = true;
10933   }
10934   return res;
10935 }
10936
10937 //================================================================================
10938 /*!
10939   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10940   \param theNodes - identifiers of nodes to be doubled
10941   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10942          nodes. If list of element identifiers is empty then nodes are doubled but
10943          they not assigned to elements
10944   \return TRUE if operation has been completed successfully, FALSE otherwise
10945 */
10946 //================================================================================
10947
10948 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10949                                     const std::list< int >& theListOfModifiedElems )
10950 {
10951   MESSAGE("DoubleNodes");
10952   myLastCreatedElems.Clear();
10953   myLastCreatedNodes.Clear();
10954
10955   if ( theListOfNodes.size() == 0 )
10956     return false;
10957
10958   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10959   if ( !aMeshDS )
10960     return false;
10961
10962   // iterate through nodes and duplicate them
10963
10964   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10965
10966   std::list< int >::const_iterator aNodeIter;
10967   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10968   {
10969     int aCurr = *aNodeIter;
10970     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10971     if ( !aNode )
10972       continue;
10973
10974     // duplicate node
10975
10976     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10977     if ( aNewNode )
10978     {
10979       anOldNodeToNewNode[ aNode ] = aNewNode;
10980       myLastCreatedNodes.Append( aNewNode );
10981     }
10982   }
10983
10984   // Create map of new nodes for modified elements
10985
10986   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10987
10988   std::list< int >::const_iterator anElemIter;
10989   for ( anElemIter = theListOfModifiedElems.begin();
10990         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10991   {
10992     int aCurr = *anElemIter;
10993     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10994     if ( !anElem )
10995       continue;
10996
10997     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10998
10999     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11000     int ind = 0;
11001     while ( anIter->more() )
11002     {
11003       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
11004       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
11005       {
11006         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
11007         aNodeArr[ ind++ ] = aNewNode;
11008       }
11009       else
11010         aNodeArr[ ind++ ] = aCurrNode;
11011     }
11012     anElemToNodes[ anElem ] = aNodeArr;
11013   }
11014
11015   // Change nodes of elements
11016
11017   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
11018     anElemToNodesIter = anElemToNodes.begin();
11019   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
11020   {
11021     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
11022     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
11023     if ( anElem )
11024       {
11025       MESSAGE("ChangeElementNodes");
11026       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
11027       }
11028   }
11029
11030   return true;
11031 }
11032
11033 namespace {
11034
11035   //================================================================================
11036   /*!
11037   \brief Check if element located inside shape
11038   \return TRUE if IN or ON shape, FALSE otherwise
11039   */
11040   //================================================================================
11041
11042   template<class Classifier>
11043   bool isInside(const SMDS_MeshElement* theElem,
11044                 Classifier&             theClassifier,
11045                 const double            theTol)
11046   {
11047     gp_XYZ centerXYZ (0, 0, 0);
11048     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11049     while (aNodeItr->more())
11050       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
11051
11052     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11053     theClassifier.Perform(aPnt, theTol);
11054     TopAbs_State aState = theClassifier.State();
11055     return (aState == TopAbs_IN || aState == TopAbs_ON );
11056   }
11057
11058   //================================================================================
11059   /*!
11060    * \brief Classifier of the 3D point on the TopoDS_Face
11061    *        with interaface suitable for isInside()
11062    */
11063   //================================================================================
11064
11065   struct _FaceClassifier
11066   {
11067     Extrema_ExtPS       _extremum;
11068     BRepAdaptor_Surface _surface;
11069     TopAbs_State        _state;
11070
11071     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11072     {
11073       _extremum.Initialize( _surface,
11074                             _surface.FirstUParameter(), _surface.LastUParameter(),
11075                             _surface.FirstVParameter(), _surface.LastVParameter(),
11076                             _surface.Tolerance(), _surface.Tolerance() );
11077     }
11078     void Perform(const gp_Pnt& aPnt, double theTol)
11079     {
11080       _state = TopAbs_OUT;
11081       _extremum.Perform(aPnt);
11082       if ( _extremum.IsDone() )
11083         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11084 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11085           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11086 #else
11087           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11088 #endif
11089     }
11090     TopAbs_State State() const
11091     {
11092       return _state;
11093     }
11094   };
11095 }
11096
11097 //================================================================================
11098 /*!
11099   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11100   This method is the first step of DoubleNodeElemGroupsInRegion.
11101   \param theElems - list of groups of elements (edges or faces) to be replicated
11102   \param theNodesNot - list of groups of nodes not to replicated
11103   \param theShape - shape to detect affected elements (element which geometric center
11104          located on or inside shape).
11105          The replicated nodes should be associated to affected elements.
11106   \return groups of affected elements
11107   \sa DoubleNodeElemGroupsInRegion()
11108  */
11109 //================================================================================
11110
11111 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11112                                                    const TIDSortedElemSet& theNodesNot,
11113                                                    const TopoDS_Shape&     theShape,
11114                                                    TIDSortedElemSet&       theAffectedElems)
11115 {
11116   if ( theShape.IsNull() )
11117     return false;
11118
11119   const double aTol = Precision::Confusion();
11120   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11121   auto_ptr<_FaceClassifier>              aFaceClassifier;
11122   if ( theShape.ShapeType() == TopAbs_SOLID )
11123   {
11124     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11125     bsc3d->PerformInfinitePoint(aTol);
11126   }
11127   else if (theShape.ShapeType() == TopAbs_FACE )
11128   {
11129     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11130   }
11131
11132   // iterates on indicated elements and get elements by back references from their nodes
11133   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11134   for ( ;  elemItr != theElems.end(); ++elemItr )
11135   {
11136     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11137     if (!anElem)
11138       continue;
11139
11140     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11141     while ( nodeItr->more() )
11142     {
11143       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11144       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11145         continue;
11146       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11147       while ( backElemItr->more() )
11148       {
11149         const SMDS_MeshElement* curElem = backElemItr->next();
11150         if ( curElem && theElems.find(curElem) == theElems.end() &&
11151              ( bsc3d.get() ?
11152                isInside( curElem, *bsc3d, aTol ) :
11153                isInside( curElem, *aFaceClassifier, aTol )))
11154           theAffectedElems.insert( curElem );
11155       }
11156     }
11157   }
11158   return true;
11159 }
11160
11161 //================================================================================
11162 /*!
11163   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11164   \param theElems - group of of elements (edges or faces) to be replicated
11165   \param theNodesNot - group of nodes not to replicate
11166   \param theShape - shape to detect affected elements (element which geometric center
11167   located on or inside shape).
11168   The replicated nodes should be associated to affected elements.
11169   \return TRUE if operation has been completed successfully, FALSE otherwise
11170 */
11171 //================================================================================
11172
11173 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11174                                             const TIDSortedElemSet& theNodesNot,
11175                                             const TopoDS_Shape&     theShape )
11176 {
11177   if ( theShape.IsNull() )
11178     return false;
11179
11180   const double aTol = Precision::Confusion();
11181   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11182   auto_ptr<_FaceClassifier>              aFaceClassifier;
11183   if ( theShape.ShapeType() == TopAbs_SOLID )
11184   {
11185     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11186     bsc3d->PerformInfinitePoint(aTol);
11187   }
11188   else if (theShape.ShapeType() == TopAbs_FACE )
11189   {
11190     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11191   }
11192
11193   // iterates on indicated elements and get elements by back references from their nodes
11194   TIDSortedElemSet anAffected;
11195   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11196   for ( ;  elemItr != theElems.end(); ++elemItr )
11197   {
11198     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11199     if (!anElem)
11200       continue;
11201
11202     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11203     while ( nodeItr->more() )
11204     {
11205       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11206       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11207         continue;
11208       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11209       while ( backElemItr->more() )
11210       {
11211         const SMDS_MeshElement* curElem = backElemItr->next();
11212         if ( curElem && theElems.find(curElem) == theElems.end() &&
11213              ( bsc3d.get() ?
11214                isInside( curElem, *bsc3d, aTol ) :
11215                isInside( curElem, *aFaceClassifier, aTol )))
11216           anAffected.insert( curElem );
11217       }
11218     }
11219   }
11220   return DoubleNodes( theElems, theNodesNot, anAffected );
11221 }
11222
11223 /*!
11224  *  \brief compute an oriented angle between two planes defined by four points.
11225  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11226  *  @param p0 base of the rotation axe
11227  *  @param p1 extremity of the rotation axe
11228  *  @param g1 belongs to the first plane
11229  *  @param g2 belongs to the second plane
11230  */
11231 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11232 {
11233 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11234 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11235 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11236 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11237   gp_Vec vref(p0, p1);
11238   gp_Vec v1(p0, g1);
11239   gp_Vec v2(p0, g2);
11240   gp_Vec n1 = vref.Crossed(v1);
11241   gp_Vec n2 = vref.Crossed(v2);
11242   return n2.AngleWithRef(n1, vref);
11243 }
11244
11245 /*!
11246  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11247  * The list of groups must describe a partition of the mesh volumes.
11248  * The nodes of the internal faces at the boundaries of the groups are doubled.
11249  * In option, the internal faces are replaced by flat elements.
11250  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11251  * The flat elements are stored in groups of volumes.
11252  * @param theElems - list of groups of volumes, where a group of volume is a set of
11253  * SMDS_MeshElements sorted by Id.
11254  * @param createJointElems - if TRUE, create the elements
11255  * @return TRUE if operation has been completed successfully, FALSE otherwise
11256  */
11257 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11258                                                      bool createJointElems)
11259 {
11260   MESSAGE("----------------------------------------------");
11261   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11262   MESSAGE("----------------------------------------------");
11263
11264   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11265   meshDS->BuildDownWardConnectivity(true);
11266   CHRONO(50);
11267   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11268
11269   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11270   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11271   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11272
11273   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11274   std::map<int,int>celldom; // cell vtkId --> domain
11275   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11276   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11277   faceDomains.clear();
11278   celldom.clear();
11279   cellDomains.clear();
11280   nodeDomains.clear();
11281   std::map<int,int> emptyMap;
11282   std::set<int> emptySet;
11283   emptyMap.clear();
11284
11285   for (int idom = 0; idom < theElems.size(); idom++)
11286     {
11287
11288       // --- build a map (face to duplicate --> volume to modify)
11289       //     with all the faces shared by 2 domains (group of elements)
11290       //     and corresponding volume of this domain, for each shared face.
11291       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11292
11293       //MESSAGE("Domain " << idom);
11294       const TIDSortedElemSet& domain = theElems[idom];
11295       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11296       for (; elemItr != domain.end(); ++elemItr)
11297         {
11298           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11299           if (!anElem)
11300             continue;
11301           int vtkId = anElem->getVtkId();
11302           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11303           int neighborsVtkIds[NBMAXNEIGHBORS];
11304           int downIds[NBMAXNEIGHBORS];
11305           unsigned char downTypes[NBMAXNEIGHBORS];
11306           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11307           for (int n = 0; n < nbNeighbors; n++)
11308             {
11309               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11310               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11311               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11312                 {
11313                   DownIdType face(downIds[n], downTypes[n]);
11314                   if (!faceDomains.count(face))
11315                     faceDomains[face] = emptyMap; // create an empty entry for face
11316                   if (!faceDomains[face].count(idom))
11317                     {
11318                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11319                       celldom[vtkId] = idom;
11320                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11321                     }
11322                 }
11323             }
11324         }
11325     }
11326
11327   //MESSAGE("Number of shared faces " << faceDomains.size());
11328   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11329
11330   // --- explore the shared faces domain by domain,
11331   //     explore the nodes of the face and see if they belong to a cell in the domain,
11332   //     which has only a node or an edge on the border (not a shared face)
11333
11334   for (int idomain = 0; idomain < theElems.size(); idomain++)
11335     {
11336       //MESSAGE("Domain " << idomain);
11337       const TIDSortedElemSet& domain = theElems[idomain];
11338       itface = faceDomains.begin();
11339       for (; itface != faceDomains.end(); ++itface)
11340         {
11341           std::map<int, int> domvol = itface->second;
11342           if (!domvol.count(idomain))
11343             continue;
11344           DownIdType face = itface->first;
11345           //MESSAGE(" --- face " << face.cellId);
11346           std::set<int> oldNodes;
11347           oldNodes.clear();
11348           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11349           std::set<int>::iterator itn = oldNodes.begin();
11350           for (; itn != oldNodes.end(); ++itn)
11351             {
11352               int oldId = *itn;
11353               //MESSAGE("     node " << oldId);
11354               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11355               for (int i=0; i<l.ncells; i++)
11356                 {
11357                   int vtkId = l.cells[i];
11358                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11359                   if (!domain.count(anElem))
11360                     continue;
11361                   int vtkType = grid->GetCellType(vtkId);
11362                   int downId = grid->CellIdToDownId(vtkId);
11363                   if (downId < 0)
11364                     {
11365                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11366                       continue; // not OK at this stage of the algorithm:
11367                                 //no cells created after BuildDownWardConnectivity
11368                     }
11369                   DownIdType aCell(downId, vtkType);
11370                   if (!cellDomains.count(aCell))
11371                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11372                   cellDomains[aCell][idomain] = vtkId;
11373                   celldom[vtkId] = idomain;
11374                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11375                 }
11376             }
11377         }
11378     }
11379
11380   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11381   //     for each shared face, get the nodes
11382   //     for each node, for each domain of the face, create a clone of the node
11383
11384   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11385   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11386   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11387
11388   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11389   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11390   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11391
11392   for (int idomain = 0; idomain < theElems.size(); idomain++)
11393     {
11394       itface = faceDomains.begin();
11395       for (; itface != faceDomains.end(); ++itface)
11396         {
11397           std::map<int, int> domvol = itface->second;
11398           if (!domvol.count(idomain))
11399             continue;
11400           DownIdType face = itface->first;
11401           //MESSAGE(" --- face " << face.cellId);
11402           std::set<int> oldNodes;
11403           oldNodes.clear();
11404           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11405           std::set<int>::iterator itn = oldNodes.begin();
11406           for (; itn != oldNodes.end(); ++itn)
11407             {
11408               int oldId = *itn;
11409               //MESSAGE("-+-+-a node " << oldId);
11410               if (!nodeDomains.count(oldId))
11411                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11412               if (nodeDomains[oldId].empty())
11413                 {
11414                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11415                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11416                 }
11417               std::map<int, int>::iterator itdom = domvol.begin();
11418               for (; itdom != domvol.end(); ++itdom)
11419                 {
11420                   int idom = itdom->first;
11421                   //MESSAGE("         domain " << idom);
11422                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11423                     {
11424                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11425                         {
11426                           vector<int> orderedDoms;
11427                           //MESSAGE("multiple node " << oldId);
11428                           if (mutipleNodes.count(oldId))
11429                             orderedDoms = mutipleNodes[oldId];
11430                           else
11431                             {
11432                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11433                               for (; it != nodeDomains[oldId].end(); ++it)
11434                                 orderedDoms.push_back(it->first);
11435                             }
11436                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11437                           //stringstream txt;
11438                           //for (int i=0; i<orderedDoms.size(); i++)
11439                           //  txt << orderedDoms[i] << " ";
11440                           //MESSAGE("orderedDoms " << txt.str());
11441                           mutipleNodes[oldId] = orderedDoms;
11442                         }
11443                       double *coords = grid->GetPoint(oldId);
11444                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11445                       int newId = newNode->getVtkId();
11446                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11447                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11448                     }
11449                 }
11450             }
11451         }
11452     }
11453
11454   for (int idomain = 0; idomain < theElems.size(); idomain++)
11455     {
11456       itface = faceDomains.begin();
11457       for (; itface != faceDomains.end(); ++itface)
11458         {
11459           std::map<int, int> domvol = itface->second;
11460           if (!domvol.count(idomain))
11461             continue;
11462           DownIdType face = itface->first;
11463           //MESSAGE(" --- face " << face.cellId);
11464           std::set<int> oldNodes;
11465           oldNodes.clear();
11466           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11467           int nbMultipleNodes = 0;
11468           std::set<int>::iterator itn = oldNodes.begin();
11469           for (; itn != oldNodes.end(); ++itn)
11470             {
11471               int oldId = *itn;
11472               if (mutipleNodes.count(oldId))
11473                 nbMultipleNodes++;
11474             }
11475           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11476             {
11477               //MESSAGE("multiple Nodes detected on a shared face");
11478               int downId = itface->first.cellId;
11479               unsigned char cellType = itface->first.cellType;
11480               // --- shared edge or shared face ?
11481               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11482                 {
11483                   int nodes[3];
11484                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11485                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11486                     if (mutipleNodes.count(nodes[i]))
11487                       if (!mutipleNodesToFace.count(nodes[i]))
11488                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11489                 }
11490               else // shared face (between two volumes)
11491                 {
11492                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11493                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11494                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11495                   for (int ie =0; ie < nbEdges; ie++)
11496                     {
11497                       int nodes[3];
11498                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11499                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11500                         {
11501                           vector<int> vn0 = mutipleNodes[nodes[0]];
11502                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11503                           vector<int> doms;
11504                           for (int i0 = 0; i0 < vn0.size(); i0++)
11505                             for (int i1 = 0; i1 < vn1.size(); i1++)
11506                               if (vn0[i0] == vn1[i1])
11507                                 doms.push_back(vn0[i0]);
11508                           if (doms.size() >2)
11509                             {
11510                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11511                               double *coords = grid->GetPoint(nodes[0]);
11512                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11513                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11514                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11515                               gp_Pnt gref;
11516                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11517                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11518                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11519                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11520                               for (int id=0; id < doms.size(); id++)
11521                                 {
11522                                   int idom = doms[id];
11523                                   for (int ivol=0; ivol<nbvol; ivol++)
11524                                     {
11525                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11526                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11527                                       if (theElems[idom].count(elem))
11528                                         {
11529                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11530                                           domvol[idom] = svol;
11531                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11532                                           double values[3];
11533                                           vtkIdType npts = 0;
11534                                           vtkIdType* pts = 0;
11535                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11536                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11537                                           if (id ==0)
11538                                             {
11539                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11540                                               angleDom[idom] = 0;
11541                                             }
11542                                           else
11543                                             {
11544                                               gp_Pnt g(values[0], values[1], values[2]);
11545                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11546                                               //MESSAGE("  angle=" << angleDom[idom]);
11547                                             }
11548                                           break;
11549                                         }
11550                                     }
11551                                 }
11552                               map<double, int> sortedDom; // sort domains by angle
11553                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11554                                 sortedDom[ia->second] = ia->first;
11555                               vector<int> vnodes;
11556                               vector<int> vdom;
11557                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11558                                 {
11559                                   vdom.push_back(ib->second);
11560                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11561                                 }
11562                               for (int ino = 0; ino < nbNodes; ino++)
11563                                 vnodes.push_back(nodes[ino]);
11564                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11565                             }
11566                         }
11567                     }
11568                 }
11569             }
11570         }
11571     }
11572
11573   // --- iterate on shared faces (volumes to modify, face to extrude)
11574   //     get node id's of the face (id SMDS = id VTK)
11575   //     create flat element with old and new nodes if requested
11576
11577   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11578   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11579
11580   std::map<int, std::map<long,int> > nodeQuadDomains;
11581   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11582
11583   if (createJointElems)
11584     {
11585       int idg;
11586       string joints2DName = "joints2D";
11587       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11588       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11589       string joints3DName = "joints3D";
11590       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11591       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11592
11593       itface = faceDomains.begin();
11594       for (; itface != faceDomains.end(); ++itface)
11595         {
11596           DownIdType face = itface->first;
11597           std::set<int> oldNodes;
11598           std::set<int>::iterator itn;
11599           oldNodes.clear();
11600           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11601
11602           std::map<int, int> domvol = itface->second;
11603           std::map<int, int>::iterator itdom = domvol.begin();
11604           int dom1 = itdom->first;
11605           int vtkVolId = itdom->second;
11606           itdom++;
11607           int dom2 = itdom->first;
11608           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11609                                                              nodeQuadDomains);
11610           stringstream grpname;
11611           grpname << "j_";
11612           if (dom1 < dom2)
11613             grpname << dom1 << "_" << dom2;
11614           else
11615             grpname << dom2 << "_" << dom1;
11616           string namegrp = grpname.str();
11617           if (!mapOfJunctionGroups.count(namegrp))
11618             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11619           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11620           if (sgrp)
11621             sgrp->Add(vol->GetID());
11622           if (vol->GetType() == SMDSAbs_Volume)
11623             joints3DGrp->Add(vol->GetID());
11624           else if (vol->GetType() == SMDSAbs_Face)
11625             joints2DGrp->Add(vol->GetID());
11626         }
11627     }
11628
11629   // --- create volumes on multiple domain intersection if requested
11630   //     iterate on mutipleNodesToFace
11631   //     iterate on edgesMultiDomains
11632
11633   if (createJointElems)
11634     {
11635       // --- iterate on mutipleNodesToFace
11636
11637       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11638       for (; itn != mutipleNodesToFace.end(); ++itn)
11639         {
11640           int node = itn->first;
11641           vector<int> orderDom = itn->second;
11642           vector<vtkIdType> orderedNodes;
11643           for (int idom = 0; idom <orderDom.size(); idom++)
11644             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11645             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11646
11647             stringstream grpname;
11648             grpname << "m2j_";
11649             grpname << 0 << "_" << 0;
11650             int idg;
11651             string namegrp = grpname.str();
11652             if (!mapOfJunctionGroups.count(namegrp))
11653               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11654             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11655             if (sgrp)
11656               sgrp->Add(face->GetID());
11657         }
11658
11659       // --- iterate on edgesMultiDomains
11660
11661       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11662       for (; ite != edgesMultiDomains.end(); ++ite)
11663         {
11664           vector<int> nodes = ite->first;
11665           vector<int> orderDom = ite->second;
11666           vector<vtkIdType> orderedNodes;
11667           if (nodes.size() == 2)
11668             {
11669               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11670               for (int ino=0; ino < nodes.size(); ino++)
11671                 if (orderDom.size() == 3)
11672                   for (int idom = 0; idom <orderDom.size(); idom++)
11673                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11674                 else
11675                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11676                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11677               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11678
11679               int idg;
11680               string namegrp = "jointsMultiples";
11681               if (!mapOfJunctionGroups.count(namegrp))
11682                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11683               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11684               if (sgrp)
11685                 sgrp->Add(vol->GetID());
11686             }
11687           else
11688             {
11689               INFOS("Quadratic multiple joints not implemented");
11690               // TODO quadratic nodes
11691             }
11692         }
11693     }
11694
11695   // --- list the explicit faces and edges of the mesh that need to be modified,
11696   //     i.e. faces and edges built with one or more duplicated nodes.
11697   //     associate these faces or edges to their corresponding domain.
11698   //     only the first domain found is kept when a face or edge is shared
11699
11700   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11701   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11702   faceOrEdgeDom.clear();
11703   feDom.clear();
11704
11705   for (int idomain = 0; idomain < theElems.size(); idomain++)
11706     {
11707       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11708       for (; itnod != nodeDomains.end(); ++itnod)
11709         {
11710           int oldId = itnod->first;
11711           //MESSAGE("     node " << oldId);
11712           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11713           for (int i = 0; i < l.ncells; i++)
11714             {
11715               int vtkId = l.cells[i];
11716               int vtkType = grid->GetCellType(vtkId);
11717               int downId = grid->CellIdToDownId(vtkId);
11718               if (downId < 0)
11719                 continue; // new cells: not to be modified
11720               DownIdType aCell(downId, vtkType);
11721               int volParents[1000];
11722               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11723               for (int j = 0; j < nbvol; j++)
11724                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11725                   if (!feDom.count(vtkId))
11726                     {
11727                       feDom[vtkId] = idomain;
11728                       faceOrEdgeDom[aCell] = emptyMap;
11729                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11730                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11731                       //        << " type " << vtkType << " downId " << downId);
11732                     }
11733             }
11734         }
11735     }
11736
11737   // --- iterate on shared faces (volumes to modify, face to extrude)
11738   //     get node id's of the face
11739   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11740
11741   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11742   for (int m=0; m<3; m++)
11743     {
11744       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11745       itface = (*amap).begin();
11746       for (; itface != (*amap).end(); ++itface)
11747         {
11748           DownIdType face = itface->first;
11749           std::set<int> oldNodes;
11750           std::set<int>::iterator itn;
11751           oldNodes.clear();
11752           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11753           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11754           std::map<int, int> localClonedNodeIds;
11755
11756           std::map<int, int> domvol = itface->second;
11757           std::map<int, int>::iterator itdom = domvol.begin();
11758           for (; itdom != domvol.end(); ++itdom)
11759             {
11760               int idom = itdom->first;
11761               int vtkVolId = itdom->second;
11762               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11763               localClonedNodeIds.clear();
11764               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11765                 {
11766                   int oldId = *itn;
11767                   if (nodeDomains[oldId].count(idom))
11768                     {
11769                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11770                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11771                     }
11772                 }
11773               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11774             }
11775         }
11776     }
11777
11778   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11779   grid->BuildLinks();
11780
11781   CHRONOSTOP(50);
11782   counters::stats();
11783   return true;
11784 }
11785
11786 /*!
11787  * \brief Double nodes on some external faces and create flat elements.
11788  * Flat elements are mainly used by some types of mechanic calculations.
11789  *
11790  * Each group of the list must be constituted of faces.
11791  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11792  * @param theElems - list of groups of faces, where a group of faces is a set of
11793  * SMDS_MeshElements sorted by Id.
11794  * @return TRUE if operation has been completed successfully, FALSE otherwise
11795  */
11796 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11797 {
11798   MESSAGE("-------------------------------------------------");
11799   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11800   MESSAGE("-------------------------------------------------");
11801
11802   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11803
11804   // --- For each group of faces
11805   //     duplicate the nodes, create a flat element based on the face
11806   //     replace the nodes of the faces by their clones
11807
11808   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11809   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11810   clonedNodes.clear();
11811   intermediateNodes.clear();
11812   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11813   mapOfJunctionGroups.clear();
11814
11815   for (int idom = 0; idom < theElems.size(); idom++)
11816     {
11817       const TIDSortedElemSet& domain = theElems[idom];
11818       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11819       for (; elemItr != domain.end(); ++elemItr)
11820         {
11821           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11822           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11823           if (!aFace)
11824             continue;
11825           // MESSAGE("aFace=" << aFace->GetID());
11826           bool isQuad = aFace->IsQuadratic();
11827           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11828
11829           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11830
11831           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11832           while (nodeIt->more())
11833             {
11834               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11835               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11836               if (isMedium)
11837                 ln2.push_back(node);
11838               else
11839                 ln0.push_back(node);
11840
11841               const SMDS_MeshNode* clone = 0;
11842               if (!clonedNodes.count(node))
11843                 {
11844                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11845                   clonedNodes[node] = clone;
11846                 }
11847               else
11848                 clone = clonedNodes[node];
11849
11850               if (isMedium)
11851                 ln3.push_back(clone);
11852               else
11853                 ln1.push_back(clone);
11854
11855               const SMDS_MeshNode* inter = 0;
11856               if (isQuad && (!isMedium))
11857                 {
11858                   if (!intermediateNodes.count(node))
11859                     {
11860                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11861                       intermediateNodes[node] = inter;
11862                     }
11863                   else
11864                     inter = intermediateNodes[node];
11865                   ln4.push_back(inter);
11866                 }
11867             }
11868
11869           // --- extrude the face
11870
11871           vector<const SMDS_MeshNode*> ln;
11872           SMDS_MeshVolume* vol = 0;
11873           vtkIdType aType = aFace->GetVtkType();
11874           switch (aType)
11875           {
11876             case VTK_TRIANGLE:
11877               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11878               // MESSAGE("vol prism " << vol->GetID());
11879               ln.push_back(ln1[0]);
11880               ln.push_back(ln1[1]);
11881               ln.push_back(ln1[2]);
11882               break;
11883             case VTK_QUAD:
11884               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11885               // MESSAGE("vol hexa " << vol->GetID());
11886               ln.push_back(ln1[0]);
11887               ln.push_back(ln1[1]);
11888               ln.push_back(ln1[2]);
11889               ln.push_back(ln1[3]);
11890               break;
11891             case VTK_QUADRATIC_TRIANGLE:
11892               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11893                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11894               // MESSAGE("vol quad prism " << vol->GetID());
11895               ln.push_back(ln1[0]);
11896               ln.push_back(ln1[1]);
11897               ln.push_back(ln1[2]);
11898               ln.push_back(ln3[0]);
11899               ln.push_back(ln3[1]);
11900               ln.push_back(ln3[2]);
11901               break;
11902             case VTK_QUADRATIC_QUAD:
11903 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11904 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11905 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11906               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11907                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11908                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11909               // MESSAGE("vol quad hexa " << vol->GetID());
11910               ln.push_back(ln1[0]);
11911               ln.push_back(ln1[1]);
11912               ln.push_back(ln1[2]);
11913               ln.push_back(ln1[3]);
11914               ln.push_back(ln3[0]);
11915               ln.push_back(ln3[1]);
11916               ln.push_back(ln3[2]);
11917               ln.push_back(ln3[3]);
11918               break;
11919             case VTK_POLYGON:
11920               break;
11921             default:
11922               break;
11923           }
11924
11925           if (vol)
11926             {
11927               stringstream grpname;
11928               grpname << "jf_";
11929               grpname << idom;
11930               int idg;
11931               string namegrp = grpname.str();
11932               if (!mapOfJunctionGroups.count(namegrp))
11933                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11934               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11935               if (sgrp)
11936                 sgrp->Add(vol->GetID());
11937             }
11938
11939           // --- modify the face
11940
11941           aFace->ChangeNodes(&ln[0], ln.size());
11942         }
11943     }
11944   return true;
11945 }
11946
11947 /*!
11948  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11949  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11950  *  groups of faces to remove inside the object, (idem edges).
11951  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11952  */
11953 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11954                                       const TopoDS_Shape& theShape,
11955                                       SMESH_NodeSearcher* theNodeSearcher,
11956                                       const char* groupName,
11957                                       std::vector<double>&   nodesCoords,
11958                                       std::vector<std::vector<int> >& listOfListOfNodes)
11959 {
11960   MESSAGE("--------------------------------");
11961   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11962   MESSAGE("--------------------------------");
11963
11964   // --- zone of volumes to remove is given :
11965   //     1 either by a geom shape (one or more vertices) and a radius,
11966   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11967   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11968   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11969   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11970   //     defined by it's name.
11971
11972   SMESHDS_GroupBase* groupDS = 0;
11973   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11974   while ( groupIt->more() )
11975     {
11976       groupDS = 0;
11977       SMESH_Group * group = groupIt->next();
11978       if ( !group ) continue;
11979       groupDS = group->GetGroupDS();
11980       if ( !groupDS || groupDS->IsEmpty() ) continue;
11981       std::string grpName = group->GetName();
11982       //MESSAGE("grpName=" << grpName);
11983       if (grpName == groupName)
11984         break;
11985       else
11986         groupDS = 0;
11987     }
11988
11989   bool isNodeGroup = false;
11990   bool isNodeCoords = false;
11991   if (groupDS)
11992     {
11993       if (groupDS->GetType() != SMDSAbs_Node)
11994         return;
11995       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11996     }
11997
11998   if (nodesCoords.size() > 0)
11999     isNodeCoords = true; // a list o nodes given by their coordinates
12000   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12001
12002   // --- define groups to build
12003
12004   int idg; // --- group of SMDS volumes
12005   string grpvName = groupName;
12006   grpvName += "_vol";
12007   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12008   if (!grp)
12009     {
12010       MESSAGE("group not created " << grpvName);
12011       return;
12012     }
12013   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12014
12015   int idgs; // --- group of SMDS faces on the skin
12016   string grpsName = groupName;
12017   grpsName += "_skin";
12018   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12019   if (!grps)
12020     {
12021       MESSAGE("group not created " << grpsName);
12022       return;
12023     }
12024   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12025
12026   int idgi; // --- group of SMDS faces internal (several shapes)
12027   string grpiName = groupName;
12028   grpiName += "_internalFaces";
12029   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12030   if (!grpi)
12031     {
12032       MESSAGE("group not created " << grpiName);
12033       return;
12034     }
12035   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12036
12037   int idgei; // --- group of SMDS faces internal (several shapes)
12038   string grpeiName = groupName;
12039   grpeiName += "_internalEdges";
12040   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12041   if (!grpei)
12042     {
12043       MESSAGE("group not created " << grpeiName);
12044       return;
12045     }
12046   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12047
12048   // --- build downward connectivity
12049
12050   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12051   meshDS->BuildDownWardConnectivity(true);
12052   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12053
12054   // --- set of volumes detected inside
12055
12056   std::set<int> setOfInsideVol;
12057   std::set<int> setOfVolToCheck;
12058
12059   std::vector<gp_Pnt> gpnts;
12060   gpnts.clear();
12061
12062   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12063     {
12064       MESSAGE("group of nodes provided");
12065       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12066       while ( elemIt->more() )
12067         {
12068           const SMDS_MeshElement* elem = elemIt->next();
12069           if (!elem)
12070             continue;
12071           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12072           if (!node)
12073             continue;
12074           SMDS_MeshElement* vol = 0;
12075           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12076           while (volItr->more())
12077             {
12078               vol = (SMDS_MeshElement*)volItr->next();
12079               setOfInsideVol.insert(vol->getVtkId());
12080               sgrp->Add(vol->GetID());
12081             }
12082         }
12083     }
12084   else if (isNodeCoords)
12085     {
12086       MESSAGE("list of nodes coordinates provided");
12087       int i = 0;
12088       int k = 0;
12089       while (i < nodesCoords.size()-2)
12090         {
12091           double x = nodesCoords[i++];
12092           double y = nodesCoords[i++];
12093           double z = nodesCoords[i++];
12094           gp_Pnt p = gp_Pnt(x, y ,z);
12095           gpnts.push_back(p);
12096           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12097         }
12098     }
12099   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12100     {
12101       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12102       TopTools_IndexedMapOfShape vertexMap;
12103       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12104       gp_Pnt p = gp_Pnt(0,0,0);
12105       if (vertexMap.Extent() < 1)
12106         return;
12107
12108       for ( int i = 1; i <= vertexMap.Extent(); ++i )
12109         {
12110           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12111           p = BRep_Tool::Pnt(vertex);
12112           gpnts.push_back(p);
12113           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12114         }
12115     }
12116
12117   if (gpnts.size() > 0)
12118     {
12119       int nodeId = 0;
12120       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12121       if (startNode)
12122         nodeId = startNode->GetID();
12123       MESSAGE("nodeId " << nodeId);
12124
12125       double radius2 = radius*radius;
12126       MESSAGE("radius2 " << radius2);
12127
12128       // --- volumes on start node
12129
12130       setOfVolToCheck.clear();
12131       SMDS_MeshElement* startVol = 0;
12132       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12133       while (volItr->more())
12134         {
12135           startVol = (SMDS_MeshElement*)volItr->next();
12136           setOfVolToCheck.insert(startVol->getVtkId());
12137         }
12138       if (setOfVolToCheck.empty())
12139         {
12140           MESSAGE("No volumes found");
12141           return;
12142         }
12143
12144       // --- starting with central volumes then their neighbors, check if they are inside
12145       //     or outside the domain, until no more new neighbor volume is inside.
12146       //     Fill the group of inside volumes
12147
12148       std::map<int, double> mapOfNodeDistance2;
12149       mapOfNodeDistance2.clear();
12150       std::set<int> setOfOutsideVol;
12151       while (!setOfVolToCheck.empty())
12152         {
12153           std::set<int>::iterator it = setOfVolToCheck.begin();
12154           int vtkId = *it;
12155           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12156           bool volInside = false;
12157           vtkIdType npts = 0;
12158           vtkIdType* pts = 0;
12159           grid->GetCellPoints(vtkId, npts, pts);
12160           for (int i=0; i<npts; i++)
12161             {
12162               double distance2 = 0;
12163               if (mapOfNodeDistance2.count(pts[i]))
12164                 {
12165                   distance2 = mapOfNodeDistance2[pts[i]];
12166                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12167                 }
12168               else
12169                 {
12170                   double *coords = grid->GetPoint(pts[i]);
12171                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12172                   distance2 = 1.E40;
12173                   for (int j=0; j<gpnts.size(); j++)
12174                     {
12175                       double d2 = aPoint.SquareDistance(gpnts[j]);
12176                       if (d2 < distance2)
12177                         {
12178                           distance2 = d2;
12179                           if (distance2 < radius2)
12180                             break;
12181                         }
12182                     }
12183                   mapOfNodeDistance2[pts[i]] = distance2;
12184                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12185                 }
12186               if (distance2 < radius2)
12187                 {
12188                   volInside = true; // one or more nodes inside the domain
12189                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12190                   break;
12191                 }
12192             }
12193           if (volInside)
12194             {
12195               setOfInsideVol.insert(vtkId);
12196               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12197               int neighborsVtkIds[NBMAXNEIGHBORS];
12198               int downIds[NBMAXNEIGHBORS];
12199               unsigned char downTypes[NBMAXNEIGHBORS];
12200               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12201               for (int n = 0; n < nbNeighbors; n++)
12202                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12203                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12204             }
12205           else
12206             {
12207               setOfOutsideVol.insert(vtkId);
12208               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12209             }
12210           setOfVolToCheck.erase(vtkId);
12211         }
12212     }
12213
12214   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12215   //     If yes, add the volume to the inside set
12216
12217   bool addedInside = true;
12218   std::set<int> setOfVolToReCheck;
12219   while (addedInside)
12220     {
12221       MESSAGE(" --------------------------- re check");
12222       addedInside = false;
12223       std::set<int>::iterator itv = setOfInsideVol.begin();
12224       for (; itv != setOfInsideVol.end(); ++itv)
12225         {
12226           int vtkId = *itv;
12227           int neighborsVtkIds[NBMAXNEIGHBORS];
12228           int downIds[NBMAXNEIGHBORS];
12229           unsigned char downTypes[NBMAXNEIGHBORS];
12230           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12231           for (int n = 0; n < nbNeighbors; n++)
12232             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12233               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12234         }
12235       setOfVolToCheck = setOfVolToReCheck;
12236       setOfVolToReCheck.clear();
12237       while  (!setOfVolToCheck.empty())
12238         {
12239           std::set<int>::iterator it = setOfVolToCheck.begin();
12240           int vtkId = *it;
12241           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12242             {
12243               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12244               int countInside = 0;
12245               int neighborsVtkIds[NBMAXNEIGHBORS];
12246               int downIds[NBMAXNEIGHBORS];
12247               unsigned char downTypes[NBMAXNEIGHBORS];
12248               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12249               for (int n = 0; n < nbNeighbors; n++)
12250                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12251                   countInside++;
12252               MESSAGE("countInside " << countInside);
12253               if (countInside > 1)
12254                 {
12255                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12256                   setOfInsideVol.insert(vtkId);
12257                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12258                   addedInside = true;
12259                 }
12260               else
12261                 setOfVolToReCheck.insert(vtkId);
12262             }
12263           setOfVolToCheck.erase(vtkId);
12264         }
12265     }
12266
12267   // --- map of Downward faces at the boundary, inside the global volume
12268   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12269   //     fill group of SMDS faces inside the volume (when several volume shapes)
12270   //     fill group of SMDS faces on the skin of the global volume (if skin)
12271
12272   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12273   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12274   std::set<int>::iterator it = setOfInsideVol.begin();
12275   for (; it != setOfInsideVol.end(); ++it)
12276     {
12277       int vtkId = *it;
12278       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12279       int neighborsVtkIds[NBMAXNEIGHBORS];
12280       int downIds[NBMAXNEIGHBORS];
12281       unsigned char downTypes[NBMAXNEIGHBORS];
12282       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12283       for (int n = 0; n < nbNeighbors; n++)
12284         {
12285           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12286           if (neighborDim == 3)
12287             {
12288               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12289                 {
12290                   DownIdType face(downIds[n], downTypes[n]);
12291                   boundaryFaces[face] = vtkId;
12292                 }
12293               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12294               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12295               if (vtkFaceId >= 0)
12296                 {
12297                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12298                   // find also the smds edges on this face
12299                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12300                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12301                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12302                   for (int i = 0; i < nbEdges; i++)
12303                     {
12304                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12305                       if (vtkEdgeId >= 0)
12306                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12307                     }
12308                 }
12309             }
12310           else if (neighborDim == 2) // skin of the volume
12311             {
12312               DownIdType face(downIds[n], downTypes[n]);
12313               skinFaces[face] = vtkId;
12314               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12315               if (vtkFaceId >= 0)
12316                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12317             }
12318         }
12319     }
12320
12321   // --- identify the edges constituting the wire of each subshape on the skin
12322   //     define polylines with the nodes of edges, equivalent to wires
12323   //     project polylines on subshapes, and partition, to get geom faces
12324
12325   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12326   std::set<int> emptySet;
12327   emptySet.clear();
12328   std::set<int> shapeIds;
12329
12330   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12331   while (itelem->more())
12332     {
12333       const SMDS_MeshElement *elem = itelem->next();
12334       int shapeId = elem->getshapeId();
12335       int vtkId = elem->getVtkId();
12336       if (!shapeIdToVtkIdSet.count(shapeId))
12337         {
12338           shapeIdToVtkIdSet[shapeId] = emptySet;
12339           shapeIds.insert(shapeId);
12340         }
12341       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12342     }
12343
12344   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12345   std::set<DownIdType, DownIdCompare> emptyEdges;
12346   emptyEdges.clear();
12347
12348   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12349   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12350     {
12351       int shapeId = itShape->first;
12352       MESSAGE(" --- Shape ID --- "<< shapeId);
12353       shapeIdToEdges[shapeId] = emptyEdges;
12354
12355       std::vector<int> nodesEdges;
12356
12357       std::set<int>::iterator its = itShape->second.begin();
12358       for (; its != itShape->second.end(); ++its)
12359         {
12360           int vtkId = *its;
12361           MESSAGE("     " << vtkId);
12362           int neighborsVtkIds[NBMAXNEIGHBORS];
12363           int downIds[NBMAXNEIGHBORS];
12364           unsigned char downTypes[NBMAXNEIGHBORS];
12365           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12366           for (int n = 0; n < nbNeighbors; n++)
12367             {
12368               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12369                 continue;
12370               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12371               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12372               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12373                 {
12374                   DownIdType edge(downIds[n], downTypes[n]);
12375                   if (!shapeIdToEdges[shapeId].count(edge))
12376                     {
12377                       shapeIdToEdges[shapeId].insert(edge);
12378                       int vtkNodeId[3];
12379                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12380                       nodesEdges.push_back(vtkNodeId[0]);
12381                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12382                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12383                     }
12384                 }
12385             }
12386         }
12387
12388       std::list<int> order;
12389       order.clear();
12390       if (nodesEdges.size() > 0)
12391         {
12392           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12393           nodesEdges[0] = -1;
12394           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12395           nodesEdges[1] = -1; // do not reuse this edge
12396           bool found = true;
12397           while (found)
12398             {
12399               int nodeTofind = order.back(); // try first to push back
12400               int i = 0;
12401               for (i = 0; i<nodesEdges.size(); i++)
12402                 if (nodesEdges[i] == nodeTofind)
12403                   break;
12404               if (i == nodesEdges.size())
12405                 found = false; // no follower found on back
12406               else
12407                 {
12408                   if (i%2) // odd ==> use the previous one
12409                     if (nodesEdges[i-1] < 0)
12410                       found = false;
12411                     else
12412                       {
12413                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12414                         nodesEdges[i-1] = -1;
12415                       }
12416                   else // even ==> use the next one
12417                     if (nodesEdges[i+1] < 0)
12418                       found = false;
12419                     else
12420                       {
12421                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12422                         nodesEdges[i+1] = -1;
12423                       }
12424                 }
12425               if (found)
12426                 continue;
12427               // try to push front
12428               found = true;
12429               nodeTofind = order.front(); // try to push front
12430               for (i = 0; i<nodesEdges.size(); i++)
12431                 if (nodesEdges[i] == nodeTofind)
12432                   break;
12433               if (i == nodesEdges.size())
12434                 {
12435                   found = false; // no predecessor found on front
12436                   continue;
12437                 }
12438               if (i%2) // odd ==> use the previous one
12439                 if (nodesEdges[i-1] < 0)
12440                   found = false;
12441                 else
12442                   {
12443                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12444                     nodesEdges[i-1] = -1;
12445                   }
12446               else // even ==> use the next one
12447                 if (nodesEdges[i+1] < 0)
12448                   found = false;
12449                 else
12450                   {
12451                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12452                     nodesEdges[i+1] = -1;
12453                   }
12454             }
12455         }
12456
12457
12458       std::vector<int> nodes;
12459       nodes.push_back(shapeId);
12460       std::list<int>::iterator itl = order.begin();
12461       for (; itl != order.end(); itl++)
12462         {
12463           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12464           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12465         }
12466       listOfListOfNodes.push_back(nodes);
12467     }
12468
12469   //     partition geom faces with blocFissure
12470   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12471   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12472
12473   return;
12474 }
12475
12476
12477 //================================================================================
12478 /*!
12479  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12480  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12481  * \return TRUE if operation has been completed successfully, FALSE otherwise
12482  */
12483 //================================================================================
12484
12485 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12486 {
12487   // iterates on volume elements and detect all free faces on them
12488   SMESHDS_Mesh* aMesh = GetMeshDS();
12489   if (!aMesh)
12490     return false;
12491   //bool res = false;
12492   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12493   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12494   while(vIt->more())
12495   {
12496     const SMDS_MeshVolume* volume = vIt->next();
12497     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12498     vTool.SetExternalNormal();
12499     //const bool isPoly = volume->IsPoly();
12500     const int iQuad = volume->IsQuadratic();
12501     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12502     {
12503       if (!vTool.IsFreeFace(iface))
12504         continue;
12505       nbFree++;
12506       vector<const SMDS_MeshNode *> nodes;
12507       int nbFaceNodes = vTool.NbFaceNodes(iface);
12508       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12509       int inode = 0;
12510       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12511         nodes.push_back(faceNodes[inode]);
12512       if (iQuad) { // add medium nodes
12513         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12514           nodes.push_back(faceNodes[inode]);
12515         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12516           nodes.push_back(faceNodes[8]);
12517       }
12518       // add new face based on volume nodes
12519       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12520         nbExisted++;
12521         continue; // face already exsist
12522       }
12523       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12524       nbCreated++;
12525     }
12526   }
12527   return ( nbFree==(nbExisted+nbCreated) );
12528 }
12529
12530 namespace
12531 {
12532   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12533   {
12534     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12535       return n;
12536     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12537   }
12538 }
12539 //================================================================================
12540 /*!
12541  * \brief Creates missing boundary elements
12542  *  \param elements - elements whose boundary is to be checked
12543  *  \param dimension - defines type of boundary elements to create
12544  *  \param group - a group to store created boundary elements in
12545  *  \param targetMesh - a mesh to store created boundary elements in
12546  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12547  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12548  *                                boundary elements will be copied into the targetMesh
12549  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12550  *                                boundary elements will be added into the new group
12551  *  \param aroundElements - if true, elements will be created on boundary of given
12552  *                          elements else, on boundary of the whole mesh.
12553  * \return nb of added boundary elements
12554  */
12555 //================================================================================
12556
12557 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12558                                        Bnd_Dimension           dimension,
12559                                        SMESH_Group*            group/*=0*/,
12560                                        SMESH_Mesh*             targetMesh/*=0*/,
12561                                        bool                    toCopyElements/*=false*/,
12562                                        bool                    toCopyExistingBoundary/*=false*/,
12563                                        bool                    toAddExistingBondary/*= false*/,
12564                                        bool                    aroundElements/*= false*/)
12565 {
12566   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12567   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12568   // hope that all elements are of the same type, do not check them all
12569   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12570     throw SALOME_Exception(LOCALIZED("wrong element type"));
12571
12572   if ( !targetMesh )
12573     toCopyElements = toCopyExistingBoundary = false;
12574
12575   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12576   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12577   int nbAddedBnd = 0;
12578
12579   // editor adding present bnd elements and optionally holding elements to add to the group
12580   SMESH_MeshEditor* presentEditor;
12581   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12582   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12583
12584   SMESH_MesherHelper helper( *myMesh );
12585   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12586   SMDS_VolumeTool vTool;
12587   TIDSortedElemSet avoidSet;
12588   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12589   int inode;
12590
12591   typedef vector<const SMDS_MeshNode*> TConnectivity;
12592
12593   SMDS_ElemIteratorPtr eIt;
12594   if (elements.empty())
12595     eIt = aMesh->elementsIterator(elemType);
12596   else
12597     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12598
12599   while (eIt->more())
12600   {
12601     const SMDS_MeshElement* elem = eIt->next();
12602     const int iQuad = elem->IsQuadratic();
12603
12604     // ------------------------------------------------------------------------------------
12605     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12606     // ------------------------------------------------------------------------------------
12607     vector<const SMDS_MeshElement*> presentBndElems;
12608     vector<TConnectivity>           missingBndElems;
12609     TConnectivity nodes;
12610     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12611     {
12612       vTool.SetExternalNormal();
12613       const SMDS_MeshElement* otherVol = 0;
12614       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12615       {
12616         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12617              ( !aroundElements || elements.count( otherVol )))
12618           continue;
12619         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12620         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12621         if ( missType == SMDSAbs_Edge ) // boundary edges
12622         {
12623           nodes.resize( 2+iQuad );
12624           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12625           {
12626             for ( int j = 0; j < nodes.size(); ++j )
12627               nodes[j] =nn[i+j];
12628             if ( const SMDS_MeshElement* edge =
12629                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12630               presentBndElems.push_back( edge );
12631             else
12632               missingBndElems.push_back( nodes );
12633           }
12634         }
12635         else // boundary face
12636         {
12637           nodes.clear();
12638           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12639             nodes.push_back( nn[inode] );
12640           if (iQuad) // add medium nodes
12641             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12642               nodes.push_back( nn[inode] );
12643           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12644           if ( iCenter > 0 )
12645             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12646
12647           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12648                                                                SMDSAbs_Face, /*noMedium=*/false ))
12649             presentBndElems.push_back( f );
12650           else
12651             missingBndElems.push_back( nodes );
12652
12653           if ( targetMesh != myMesh )
12654           {
12655             // add 1D elements on face boundary to be added to a new mesh
12656             const SMDS_MeshElement* edge;
12657             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12658             {
12659               if ( iQuad )
12660                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12661               else
12662                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12663               if ( edge && avoidSet.insert( edge ).second )
12664                 presentBndElems.push_back( edge );
12665             }
12666           }
12667         }
12668       }
12669     }
12670     else                     // elem is a face ------------------------------------------
12671     {
12672       avoidSet.clear(), avoidSet.insert( elem );
12673       int nbNodes = elem->NbCornerNodes();
12674       nodes.resize( 2 /*+ iQuad*/);
12675       for ( int i = 0; i < nbNodes; i++ )
12676       {
12677         nodes[0] = elem->GetNode(i);
12678         nodes[1] = elem->GetNode((i+1)%nbNodes);
12679         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12680           continue; // not free link
12681
12682         //if ( iQuad )
12683         //nodes[2] = elem->GetNode( i + nbNodes );
12684         if ( const SMDS_MeshElement* edge =
12685              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12686           presentBndElems.push_back( edge );
12687         else
12688           missingBndElems.push_back( nodes );
12689       }
12690     }
12691
12692     // ---------------------------------
12693     // 2. Add missing boundary elements
12694     // ---------------------------------
12695     if ( targetMesh != myMesh )
12696       // instead of making a map of nodes in this mesh and targetMesh,
12697       // we create nodes with same IDs.
12698       for ( int i = 0; i < missingBndElems.size(); ++i )
12699       {
12700         TConnectivity& srcNodes = missingBndElems[i];
12701         TConnectivity  nodes( srcNodes.size() );
12702         for ( inode = 0; inode < nodes.size(); ++inode )
12703           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12704         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12705                                                                    missType,
12706                                                                    /*noMedium=*/false))
12707           continue;
12708         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12709         ++nbAddedBnd;
12710       }
12711     else
12712       for ( int i = 0; i < missingBndElems.size(); ++i )
12713       {
12714         TConnectivity& nodes = missingBndElems[i];
12715         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12716                                                                    missType,
12717                                                                    /*noMedium=*/false))
12718           continue;
12719         SMDS_MeshElement* elem =
12720           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12721         ++nbAddedBnd;
12722
12723         // try to set a new element to a shape
12724         if ( myMesh->HasShapeToMesh() )
12725         {
12726           bool ok = true;
12727           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12728           const int nbN = nodes.size() / (iQuad+1 );
12729           for ( inode = 0; inode < nbN && ok; ++inode )
12730           {
12731             pair<int, TopAbs_ShapeEnum> i_stype =
12732               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12733             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12734               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12735           }
12736           if ( ok && mediumShapes.size() > 1 )
12737           {
12738             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12739             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12740             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12741             {
12742               if (( ok = ( stype_i->first != stype_i_0.first )))
12743                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12744                                         aMesh->IndexToShape( stype_i_0.second ));
12745             }
12746           }
12747           if ( ok && mediumShapes.begin()->first == missShapeType )
12748             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12749         }
12750       }
12751
12752     // ----------------------------------
12753     // 3. Copy present boundary elements
12754     // ----------------------------------
12755     if ( toCopyExistingBoundary )
12756       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12757       {
12758         const SMDS_MeshElement* e = presentBndElems[i];
12759         TConnectivity nodes( e->NbNodes() );
12760         for ( inode = 0; inode < nodes.size(); ++inode )
12761           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12762         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12763       }
12764     else // store present elements to add them to a group
12765       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12766       {
12767         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12768       }
12769
12770   } // loop on given elements
12771
12772   // ---------------------------------------------
12773   // 4. Fill group with boundary elements
12774   // ---------------------------------------------
12775   if ( group )
12776   {
12777     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12778       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12779         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12780   }
12781   tgtEditor.myLastCreatedElems.Clear();
12782   tgtEditor2.myLastCreatedElems.Clear();
12783
12784   // -----------------------
12785   // 5. Copy given elements
12786   // -----------------------
12787   if ( toCopyElements && targetMesh != myMesh )
12788   {
12789     if (elements.empty())
12790       eIt = aMesh->elementsIterator(elemType);
12791     else
12792       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12793     while (eIt->more())
12794     {
12795       const SMDS_MeshElement* elem = eIt->next();
12796       TConnectivity nodes( elem->NbNodes() );
12797       for ( inode = 0; inode < nodes.size(); ++inode )
12798         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12799       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12800
12801       tgtEditor.myLastCreatedElems.Clear();
12802     }
12803   }
12804   return nbAddedBnd;
12805 }