Salome HOME
Merge from V6_main 01/04/2013
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2013  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         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7660         // after MergeNodes() w/o creating node in place of merged ones.
7661         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7662         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7663           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7664             sm->SetIsAlwaysComputed( true );
7665       }
7666
7667       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7668       while ( invElemIt->more() ) {
7669         const SMDS_MeshElement* elem = invElemIt->next();
7670         elems.insert(elem);
7671       }
7672     }
7673   }
7674   // Change element nodes or remove an element
7675
7676   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7677   for ( ; eIt != elems.end(); eIt++ ) {
7678     const SMDS_MeshElement* elem = *eIt;
7679     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7680     int nbNodes = elem->NbNodes();
7681     int aShapeId = FindShape( elem );
7682
7683     set<const SMDS_MeshNode*> nodeSet;
7684     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7685     int iUnique = 0, iCur = 0, nbRepl = 0;
7686     vector<int> iRepl( nbNodes );
7687
7688     // get new seq of nodes
7689     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7690     while ( itN->more() ) {
7691       const SMDS_MeshNode* n =
7692         static_cast<const SMDS_MeshNode*>( itN->next() );
7693
7694       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7695       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7696         n = (*nnIt).second;
7697         // BUG 0020185: begin
7698         {
7699           bool stopRecur = false;
7700           set<const SMDS_MeshNode*> nodesRecur;
7701           nodesRecur.insert(n);
7702           while (!stopRecur) {
7703             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7704             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7705               n = (*nnIt_i).second;
7706               if (!nodesRecur.insert(n).second) {
7707                 // error: recursive dependancy
7708                 stopRecur = true;
7709               }
7710             }
7711             else
7712               stopRecur = true;
7713           }
7714         }
7715         // BUG 0020185: end
7716       }
7717       curNodes[ iCur ] = n;
7718       bool isUnique = nodeSet.insert( n ).second;
7719       if ( isUnique )
7720         uniqueNodes[ iUnique++ ] = n;
7721       else
7722         iRepl[ nbRepl++ ] = iCur;
7723       iCur++;
7724     }
7725
7726     // Analyse element topology after replacement
7727
7728     bool isOk = true;
7729     int nbUniqueNodes = nodeSet.size();
7730     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7731     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7732       // Polygons and Polyhedral volumes
7733       if (elem->IsPoly()) {
7734
7735         if (elem->GetType() == SMDSAbs_Face) {
7736           // Polygon
7737           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7738           int inode = 0;
7739           for (; inode < nbNodes; inode++) {
7740             face_nodes[inode] = curNodes[inode];
7741           }
7742
7743           vector<const SMDS_MeshNode *> polygons_nodes;
7744           vector<int> quantities;
7745           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7746           if (nbNew > 0) {
7747             inode = 0;
7748             for (int iface = 0; iface < nbNew; iface++) {
7749               int nbNodes = quantities[iface];
7750               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7751               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7752                 poly_nodes[ii] = polygons_nodes[inode];
7753               }
7754               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7755               myLastCreatedElems.Append(newElem);
7756               if (aShapeId)
7757                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7758             }
7759
7760             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7761             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7762             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7763             int quid =0;
7764             if (nbNew > 0) quid = nbNew - 1;
7765             vector<int> newquant(quantities.begin()+quid, quantities.end());
7766             const SMDS_MeshElement* newElem = 0;
7767             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7768             myLastCreatedElems.Append(newElem);
7769             if ( aShapeId && newElem )
7770               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7771             rmElemIds.push_back(elem->GetID());
7772           }
7773           else {
7774             rmElemIds.push_back(elem->GetID());
7775           }
7776
7777         }
7778         else if (elem->GetType() == SMDSAbs_Volume) {
7779           // Polyhedral volume
7780           if (nbUniqueNodes < 4) {
7781             rmElemIds.push_back(elem->GetID());
7782           }
7783           else {
7784             // each face has to be analyzed in order to check volume validity
7785             const SMDS_VtkVolume* aPolyedre =
7786               dynamic_cast<const SMDS_VtkVolume*>( elem );
7787             if (aPolyedre) {
7788               int nbFaces = aPolyedre->NbFaces();
7789
7790               vector<const SMDS_MeshNode *> poly_nodes;
7791               vector<int> quantities;
7792
7793               for (int iface = 1; iface <= nbFaces; iface++) {
7794                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7795                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7796
7797                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7798                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7799                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7800                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7801                     faceNode = (*nnIt).second;
7802                   }
7803                   faceNodes[inode - 1] = faceNode;
7804                 }
7805
7806                 SimplifyFace(faceNodes, poly_nodes, quantities);
7807               }
7808
7809               if (quantities.size() > 3) {
7810                 // to be done: remove coincident faces
7811               }
7812
7813               if (quantities.size() > 3)
7814                 {
7815                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7816                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7817                   const SMDS_MeshElement* newElem = 0;
7818                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7819                   myLastCreatedElems.Append(newElem);
7820                   if ( aShapeId && newElem )
7821                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7822                   rmElemIds.push_back(elem->GetID());
7823                 }
7824             }
7825             else {
7826               rmElemIds.push_back(elem->GetID());
7827             }
7828           }
7829         }
7830         else {
7831         }
7832
7833         continue;
7834       } // poly element
7835
7836       // Regular elements
7837       // TODO not all the possible cases are solved. Find something more generic?
7838       switch ( nbNodes ) {
7839       case 2: ///////////////////////////////////// EDGE
7840         isOk = false; break;
7841       case 3: ///////////////////////////////////// TRIANGLE
7842         isOk = false; break;
7843       case 4:
7844         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7845           isOk = false;
7846         else { //////////////////////////////////// QUADRANGLE
7847           if ( nbUniqueNodes < 3 )
7848             isOk = false;
7849           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7850             isOk = false; // opposite nodes stick
7851           //MESSAGE("isOk " << isOk);
7852         }
7853         break;
7854       case 6: ///////////////////////////////////// PENTAHEDRON
7855         if ( nbUniqueNodes == 4 ) {
7856           // ---------------------------------> tetrahedron
7857           if (nbRepl == 3 &&
7858               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7859             // all top nodes stick: reverse a bottom
7860             uniqueNodes[ 0 ] = curNodes [ 1 ];
7861             uniqueNodes[ 1 ] = curNodes [ 0 ];
7862           }
7863           else if (nbRepl == 3 &&
7864                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7865             // all bottom nodes stick: set a top before
7866             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7867             uniqueNodes[ 0 ] = curNodes [ 3 ];
7868             uniqueNodes[ 1 ] = curNodes [ 4 ];
7869             uniqueNodes[ 2 ] = curNodes [ 5 ];
7870           }
7871           else if (nbRepl == 4 &&
7872                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7873             // a lateral face turns into a line: reverse a bottom
7874             uniqueNodes[ 0 ] = curNodes [ 1 ];
7875             uniqueNodes[ 1 ] = curNodes [ 0 ];
7876           }
7877           else
7878             isOk = false;
7879         }
7880         else if ( nbUniqueNodes == 5 ) {
7881           // PENTAHEDRON --------------------> 2 tetrahedrons
7882           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7883             // a bottom node sticks with a linked top one
7884             // 1.
7885             SMDS_MeshElement* newElem =
7886               aMesh->AddVolume(curNodes[ 3 ],
7887                                curNodes[ 4 ],
7888                                curNodes[ 5 ],
7889                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7890             myLastCreatedElems.Append(newElem);
7891             if ( aShapeId )
7892               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7893             // 2. : reverse a bottom
7894             uniqueNodes[ 0 ] = curNodes [ 1 ];
7895             uniqueNodes[ 1 ] = curNodes [ 0 ];
7896             nbUniqueNodes = 4;
7897           }
7898           else
7899             isOk = false;
7900         }
7901         else
7902           isOk = false;
7903         break;
7904       case 8: {
7905         if(elem->IsQuadratic()) { // Quadratic quadrangle
7906           //   1    5    2
7907           //    +---+---+
7908           //    |       |
7909           //    |       |
7910           //   4+       +6
7911           //    |       |
7912           //    |       |
7913           //    +---+---+
7914           //   0    7    3
7915           isOk = false;
7916           if(nbRepl==2) {
7917             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7918           }
7919           if(nbRepl==3) {
7920             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7921             nbUniqueNodes = 6;
7922             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7923               uniqueNodes[0] = curNodes[0];
7924               uniqueNodes[1] = curNodes[2];
7925               uniqueNodes[2] = curNodes[3];
7926               uniqueNodes[3] = curNodes[5];
7927               uniqueNodes[4] = curNodes[6];
7928               uniqueNodes[5] = curNodes[7];
7929               isOk = true;
7930             }
7931             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7932               uniqueNodes[0] = curNodes[0];
7933               uniqueNodes[1] = curNodes[1];
7934               uniqueNodes[2] = curNodes[2];
7935               uniqueNodes[3] = curNodes[4];
7936               uniqueNodes[4] = curNodes[5];
7937               uniqueNodes[5] = curNodes[6];
7938               isOk = true;
7939             }
7940             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7941               uniqueNodes[0] = curNodes[1];
7942               uniqueNodes[1] = curNodes[2];
7943               uniqueNodes[2] = curNodes[3];
7944               uniqueNodes[3] = curNodes[5];
7945               uniqueNodes[4] = curNodes[6];
7946               uniqueNodes[5] = curNodes[0];
7947               isOk = true;
7948             }
7949             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7950               uniqueNodes[0] = curNodes[0];
7951               uniqueNodes[1] = curNodes[1];
7952               uniqueNodes[2] = curNodes[3];
7953               uniqueNodes[3] = curNodes[4];
7954               uniqueNodes[4] = curNodes[6];
7955               uniqueNodes[5] = curNodes[7];
7956               isOk = true;
7957             }
7958             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7959               uniqueNodes[0] = curNodes[0];
7960               uniqueNodes[1] = curNodes[2];
7961               uniqueNodes[2] = curNodes[3];
7962               uniqueNodes[3] = curNodes[1];
7963               uniqueNodes[4] = curNodes[6];
7964               uniqueNodes[5] = curNodes[7];
7965               isOk = true;
7966             }
7967             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7968               uniqueNodes[0] = curNodes[0];
7969               uniqueNodes[1] = curNodes[1];
7970               uniqueNodes[2] = curNodes[2];
7971               uniqueNodes[3] = curNodes[4];
7972               uniqueNodes[4] = curNodes[5];
7973               uniqueNodes[5] = curNodes[7];
7974               isOk = true;
7975             }
7976             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7977               uniqueNodes[0] = curNodes[0];
7978               uniqueNodes[1] = curNodes[1];
7979               uniqueNodes[2] = curNodes[3];
7980               uniqueNodes[3] = curNodes[4];
7981               uniqueNodes[4] = curNodes[2];
7982               uniqueNodes[5] = curNodes[7];
7983               isOk = true;
7984             }
7985             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7986               uniqueNodes[0] = curNodes[0];
7987               uniqueNodes[1] = curNodes[1];
7988               uniqueNodes[2] = curNodes[2];
7989               uniqueNodes[3] = curNodes[4];
7990               uniqueNodes[4] = curNodes[5];
7991               uniqueNodes[5] = curNodes[3];
7992               isOk = true;
7993             }
7994           }
7995           if(nbRepl==4) {
7996             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7997           }
7998           if(nbRepl==5) {
7999             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
8000           }
8001           break;
8002         }
8003         //////////////////////////////////// HEXAHEDRON
8004         isOk = false;
8005         SMDS_VolumeTool hexa (elem);
8006         hexa.SetExternalNormal();
8007         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
8008           //////////////////////// HEX ---> 1 tetrahedron
8009           for ( int iFace = 0; iFace < 6; iFace++ ) {
8010             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8011             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8012                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8013                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8014               // one face turns into a point ...
8015               int iOppFace = hexa.GetOppFaceIndex( iFace );
8016               ind = hexa.GetFaceNodesIndices( iOppFace );
8017               int nbStick = 0;
8018               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
8019                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8020                   nbStick++;
8021               }
8022               if ( nbStick == 1 ) {
8023                 // ... and the opposite one - into a triangle.
8024                 // set a top node
8025                 ind = hexa.GetFaceNodesIndices( iFace );
8026                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
8027                 isOk = true;
8028               }
8029               break;
8030             }
8031           }
8032         }
8033         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
8034           //////////////////////// HEX ---> 1 prism
8035           int nbTria = 0, iTria[3];
8036           const int *ind; // indices of face nodes
8037           // look for triangular faces
8038           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
8039             ind = hexa.GetFaceNodesIndices( iFace );
8040             TIDSortedNodeSet faceNodes;
8041             for ( iCur = 0; iCur < 4; iCur++ )
8042               faceNodes.insert( curNodes[ind[iCur]] );
8043             if ( faceNodes.size() == 3 )
8044               iTria[ nbTria++ ] = iFace;
8045           }
8046           // check if triangles are opposite
8047           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
8048           {
8049             isOk = true;
8050             // set nodes of the bottom triangle
8051             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
8052             vector<int> indB;
8053             for ( iCur = 0; iCur < 4; iCur++ )
8054               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
8055                 indB.push_back( ind[iCur] );
8056             if ( !hexa.IsForward() )
8057               std::swap( indB[0], indB[2] );
8058             for ( iCur = 0; iCur < 3; iCur++ )
8059               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
8060             // set nodes of the top triangle
8061             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
8062             for ( iCur = 0; iCur < 3; ++iCur )
8063               for ( int j = 0; j < 4; ++j )
8064                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
8065                 {
8066                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
8067                   break;
8068                 }
8069           }
8070           break;
8071         }
8072         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
8073           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
8074           for ( int iFace = 0; iFace < 6; iFace++ ) {
8075             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8076             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
8077                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
8078                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
8079               // one face turns into a point ...
8080               int iOppFace = hexa.GetOppFaceIndex( iFace );
8081               ind = hexa.GetFaceNodesIndices( iOppFace );
8082               int nbStick = 0;
8083               iUnique = 2;  // reverse a tetrahedron 1 bottom
8084               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
8085                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
8086                   nbStick++;
8087                 else if ( iUnique >= 0 )
8088                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
8089               }
8090               if ( nbStick == 0 ) {
8091                 // ... and the opposite one is a quadrangle
8092                 // set a top node
8093                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
8094                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
8095                 nbUniqueNodes = 4;
8096                 // tetrahedron 2
8097                 SMDS_MeshElement* newElem =
8098                   aMesh->AddVolume(curNodes[ind[ 0 ]],
8099                                    curNodes[ind[ 3 ]],
8100                                    curNodes[ind[ 2 ]],
8101                                    curNodes[indTop[ 0 ]]);
8102                 myLastCreatedElems.Append(newElem);
8103                 if ( aShapeId )
8104                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
8105                 isOk = true;
8106               }
8107               break;
8108             }
8109           }
8110         }
8111         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
8112           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
8113           // find indices of quad and tri faces
8114           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
8115           for ( iFace = 0; iFace < 6; iFace++ ) {
8116             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
8117             nodeSet.clear();
8118             for ( iCur = 0; iCur < 4; iCur++ )
8119               nodeSet.insert( curNodes[ind[ iCur ]] );
8120             nbUniqueNodes = nodeSet.size();
8121             if ( nbUniqueNodes == 3 )
8122               iTriFace[ nbTri++ ] = iFace;
8123             else if ( nbUniqueNodes == 4 )
8124               iQuadFace[ nbQuad++ ] = iFace;
8125           }
8126           if (nbQuad == 2 && nbTri == 4 &&
8127               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
8128             // 2 opposite quadrangles stuck with a diagonal;
8129             // sample groups of merged indices: (0-4)(2-6)
8130             // --------------------------------------------> 2 tetrahedrons
8131             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
8132             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
8133             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
8134             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
8135                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
8136               // stuck with 0-2 diagonal
8137               i0  = ind1[ 3 ];
8138               i1d = ind1[ 0 ];
8139               i2  = ind1[ 1 ];
8140               i3d = ind1[ 2 ];
8141               i0t = ind2[ 1 ];
8142               i2t = ind2[ 3 ];
8143             }
8144             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
8145                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
8146               // stuck with 1-3 diagonal
8147               i0  = ind1[ 0 ];
8148               i1d = ind1[ 1 ];
8149               i2  = ind1[ 2 ];
8150               i3d = ind1[ 3 ];
8151               i0t = ind2[ 0 ];
8152               i2t = ind2[ 1 ];
8153             }
8154             else {
8155               ASSERT(0);
8156             }
8157             // tetrahedron 1
8158             uniqueNodes[ 0 ] = curNodes [ i0 ];
8159             uniqueNodes[ 1 ] = curNodes [ i1d ];
8160             uniqueNodes[ 2 ] = curNodes [ i3d ];
8161             uniqueNodes[ 3 ] = curNodes [ i0t ];
8162             nbUniqueNodes = 4;
8163             // tetrahedron 2
8164             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8165                                                          curNodes[ i2 ],
8166                                                          curNodes[ i3d ],
8167                                                          curNodes[ i2t ]);
8168             myLastCreatedElems.Append(newElem);
8169             if ( aShapeId )
8170               aMesh->SetMeshElementOnShape( newElem, aShapeId );
8171             isOk = true;
8172           }
8173           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8174                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8175             // --------------------------------------------> prism
8176             // find 2 opposite triangles
8177             nbUniqueNodes = 6;
8178             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8179               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8180                 // find indices of kept and replaced nodes
8181                 // and fill unique nodes of 2 opposite triangles
8182                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8183                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8184                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8185                 // fill unique nodes
8186                 iUnique = 0;
8187                 isOk = true;
8188                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8189                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
8190                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8191                   if ( n == nInit ) {
8192                     // iCur of a linked node of the opposite face (make normals co-directed):
8193                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8194                     // check that correspondent corners of triangles are linked
8195                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8196                       isOk = false;
8197                     else {
8198                       uniqueNodes[ iUnique ] = n;
8199                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8200                       iUnique++;
8201                     }
8202                   }
8203                 }
8204                 break;
8205               }
8206             }
8207           }
8208         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8209         else
8210         {
8211           MESSAGE("MergeNodes() removes hexahedron "<< elem);
8212         }
8213         break;
8214       } // HEXAHEDRON
8215
8216       default:
8217         isOk = false;
8218       } // switch ( nbNodes )
8219
8220     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8221
8222     if ( isOk ) { // the elem remains valid after sticking nodes
8223       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8224       {
8225         // Change nodes of polyedre
8226         const SMDS_VtkVolume* aPolyedre =
8227           dynamic_cast<const SMDS_VtkVolume*>( elem );
8228         if (aPolyedre) {
8229           int nbFaces = aPolyedre->NbFaces();
8230
8231           vector<const SMDS_MeshNode *> poly_nodes;
8232           vector<int> quantities (nbFaces);
8233
8234           for (int iface = 1; iface <= nbFaces; iface++) {
8235             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8236             quantities[iface - 1] = nbFaceNodes;
8237
8238             for (inode = 1; inode <= nbFaceNodes; inode++) {
8239               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8240
8241               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8242               if (nnIt != nodeNodeMap.end()) { // curNode sticks
8243                 curNode = (*nnIt).second;
8244               }
8245               poly_nodes.push_back(curNode);
8246             }
8247           }
8248           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8249         }
8250       }
8251       else // replace non-polyhedron elements
8252       {
8253         const SMDSAbs_ElementType etyp = elem->GetType();
8254         const int elemId               = elem->GetID();
8255         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
8256         uniqueNodes.resize(nbUniqueNodes);
8257
8258         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8259
8260         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8261         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8262         if ( sm && newElem )
8263           sm->AddElement( newElem );
8264         if ( elem != newElem )
8265           ReplaceElemInGroups( elem, newElem, aMesh );
8266       }
8267     }
8268     else {
8269       // Remove invalid regular element or invalid polygon
8270       rmElemIds.push_back( elem->GetID() );
8271     }
8272
8273   } // loop on elements
8274
8275   // Remove bad elements, then equal nodes (order important)
8276
8277   Remove( rmElemIds, false );
8278   Remove( rmNodeIds, true );
8279
8280 }
8281
8282
8283 // ========================================================
8284 // class   : SortableElement
8285 // purpose : allow sorting elements basing on their nodes
8286 // ========================================================
8287 class SortableElement : public set <const SMDS_MeshElement*>
8288 {
8289 public:
8290
8291   SortableElement( const SMDS_MeshElement* theElem )
8292   {
8293     myElem = theElem;
8294     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8295     while ( nodeIt->more() )
8296       this->insert( nodeIt->next() );
8297   }
8298
8299   const SMDS_MeshElement* Get() const
8300   { return myElem; }
8301
8302   void Set(const SMDS_MeshElement* e) const
8303   { myElem = e; }
8304
8305
8306 private:
8307   mutable const SMDS_MeshElement* myElem;
8308 };
8309
8310 //=======================================================================
8311 //function : FindEqualElements
8312 //purpose  : Return list of group of elements built on the same nodes.
8313 //           Search among theElements or in the whole mesh if theElements is empty
8314 //=======================================================================
8315
8316 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
8317                                          TListOfListOfElementsID & theGroupsOfElementsID)
8318 {
8319   myLastCreatedElems.Clear();
8320   myLastCreatedNodes.Clear();
8321
8322   typedef map< SortableElement, int > TMapOfNodeSet;
8323   typedef list<int> TGroupOfElems;
8324
8325   if ( theElements.empty() )
8326   { // get all elements in the mesh
8327     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8328     while ( eIt->more() )
8329       theElements.insert( theElements.end(), eIt->next());
8330   }
8331
8332   vector< TGroupOfElems > arrayOfGroups;
8333   TGroupOfElems groupOfElems;
8334   TMapOfNodeSet mapOfNodeSet;
8335
8336   TIDSortedElemSet::iterator elemIt = theElements.begin();
8337   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
8338     const SMDS_MeshElement* curElem = *elemIt;
8339     SortableElement SE(curElem);
8340     int ind = -1;
8341     // check uniqueness
8342     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8343     if( !(pp.second) ) {
8344       TMapOfNodeSet::iterator& itSE = pp.first;
8345       ind = (*itSE).second;
8346       arrayOfGroups[ind].push_back(curElem->GetID());
8347     }
8348     else {
8349       groupOfElems.clear();
8350       groupOfElems.push_back(curElem->GetID());
8351       arrayOfGroups.push_back(groupOfElems);
8352       i++;
8353     }
8354   }
8355
8356   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8357   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8358     groupOfElems = *groupIt;
8359     if ( groupOfElems.size() > 1 ) {
8360       groupOfElems.sort();
8361       theGroupsOfElementsID.push_back(groupOfElems);
8362     }
8363   }
8364 }
8365
8366 //=======================================================================
8367 //function : MergeElements
8368 //purpose  : In each given group, substitute all elements by the first one.
8369 //=======================================================================
8370
8371 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8372 {
8373   myLastCreatedElems.Clear();
8374   myLastCreatedNodes.Clear();
8375
8376   typedef list<int> TListOfIDs;
8377   TListOfIDs rmElemIds; // IDs of elems to remove
8378
8379   SMESHDS_Mesh* aMesh = GetMeshDS();
8380
8381   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8382   while ( groupsIt != theGroupsOfElementsID.end() ) {
8383     TListOfIDs& aGroupOfElemID = *groupsIt;
8384     aGroupOfElemID.sort();
8385     int elemIDToKeep = aGroupOfElemID.front();
8386     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8387     aGroupOfElemID.pop_front();
8388     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8389     while ( idIt != aGroupOfElemID.end() ) {
8390       int elemIDToRemove = *idIt;
8391       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8392       // add the kept element in groups of removed one (PAL15188)
8393       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8394       rmElemIds.push_back( elemIDToRemove );
8395       ++idIt;
8396     }
8397     ++groupsIt;
8398   }
8399
8400   Remove( rmElemIds, false );
8401 }
8402
8403 //=======================================================================
8404 //function : MergeEqualElements
8405 //purpose  : Remove all but one of elements built on the same nodes.
8406 //=======================================================================
8407
8408 void SMESH_MeshEditor::MergeEqualElements()
8409 {
8410   TIDSortedElemSet aMeshElements; /* empty input ==
8411                                      to merge equal elements in the whole mesh */
8412   TListOfListOfElementsID aGroupsOfElementsID;
8413   FindEqualElements(aMeshElements, aGroupsOfElementsID);
8414   MergeElements(aGroupsOfElementsID);
8415 }
8416
8417 //=======================================================================
8418 //function : FindFaceInSet
8419 //purpose  : Return a face having linked nodes n1 and n2 and which is
8420 //           - not in avoidSet,
8421 //           - in elemSet provided that !elemSet.empty()
8422 //           i1 and i2 optionally returns indices of n1 and n2
8423 //=======================================================================
8424
8425 const SMDS_MeshElement*
8426 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode*    n1,
8427                                 const SMDS_MeshNode*    n2,
8428                                 const TIDSortedElemSet& elemSet,
8429                                 const TIDSortedElemSet& avoidSet,
8430                                 int*                    n1ind,
8431                                 int*                    n2ind)
8432
8433 {
8434   int i1, i2;
8435   const SMDS_MeshElement* face = 0;
8436
8437   SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8438   //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8439   while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8440   {
8441     //MESSAGE("in while ( invElemIt->more() && !face )");
8442     const SMDS_MeshElement* elem = invElemIt->next();
8443     if (avoidSet.count( elem ))
8444       continue;
8445     if ( !elemSet.empty() && !elemSet.count( elem ))
8446       continue;
8447     // index of n1
8448     i1 = elem->GetNodeIndex( n1 );
8449     // find a n2 linked to n1
8450     int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8451     for ( int di = -1; di < 2 && !face; di += 2 )
8452     {
8453       i2 = (i1+di+nbN) % nbN;
8454       if ( elem->GetNode( i2 ) == n2 )
8455         face = elem;
8456     }
8457     if ( !face && elem->IsQuadratic())
8458     {
8459       // analysis for quadratic elements using all nodes
8460       const SMDS_VtkFace* F =
8461         dynamic_cast<const SMDS_VtkFace*>(elem);
8462       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8463       // use special nodes iterator
8464       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8465       const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8466       for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8467       {
8468         const SMDS_MeshNode* n = cast2Node( anIter->next() );
8469         if ( n1 == prevN && n2 == n )
8470         {
8471           face = elem;
8472         }
8473         else if ( n2 == prevN && n1 == n )
8474         {
8475           face = elem; swap( i1, i2 );
8476         }
8477         prevN = n;
8478       }
8479     }
8480   }
8481   if ( n1ind ) *n1ind = i1;
8482   if ( n2ind ) *n2ind = i2;
8483   return face;
8484 }
8485
8486 //=======================================================================
8487 //function : findAdjacentFace
8488 //purpose  :
8489 //=======================================================================
8490
8491 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8492                                                 const SMDS_MeshNode* n2,
8493                                                 const SMDS_MeshElement* elem)
8494 {
8495   TIDSortedElemSet elemSet, avoidSet;
8496   if ( elem )
8497     avoidSet.insert ( elem );
8498   return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8499 }
8500
8501 //=======================================================================
8502 //function : FindFreeBorder
8503 //purpose  :
8504 //=======================================================================
8505
8506 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8507
8508 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
8509                                        const SMDS_MeshNode*             theSecondNode,
8510                                        const SMDS_MeshNode*             theLastNode,
8511                                        list< const SMDS_MeshNode* > &   theNodes,
8512                                        list< const SMDS_MeshElement* >& theFaces)
8513 {
8514   if ( !theFirstNode || !theSecondNode )
8515     return false;
8516   // find border face between theFirstNode and theSecondNode
8517   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8518   if ( !curElem )
8519     return false;
8520
8521   theFaces.push_back( curElem );
8522   theNodes.push_back( theFirstNode );
8523   theNodes.push_back( theSecondNode );
8524
8525   //vector<const SMDS_MeshNode*> nodes;
8526   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8527   TIDSortedElemSet foundElems;
8528   bool needTheLast = ( theLastNode != 0 );
8529
8530   while ( nStart != theLastNode ) {
8531     if ( nStart == theFirstNode )
8532       return !needTheLast;
8533
8534     // find all free border faces sharing form nStart
8535
8536     list< const SMDS_MeshElement* > curElemList;
8537     list< const SMDS_MeshNode* > nStartList;
8538     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8539     while ( invElemIt->more() ) {
8540       const SMDS_MeshElement* e = invElemIt->next();
8541       if ( e == curElem || foundElems.insert( e ).second ) {
8542         // get nodes
8543         int iNode = 0, nbNodes = e->NbNodes();
8544         //const SMDS_MeshNode* nodes[nbNodes+1];
8545         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8546
8547         if(e->IsQuadratic()) {
8548           const SMDS_VtkFace* F =
8549             dynamic_cast<const SMDS_VtkFace*>(e);
8550           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8551           // use special nodes iterator
8552           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8553           while( anIter->more() ) {
8554             nodes[ iNode++ ] = cast2Node(anIter->next());
8555           }
8556         }
8557         else {
8558           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8559           while ( nIt->more() )
8560             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8561         }
8562         nodes[ iNode ] = nodes[ 0 ];
8563         // check 2 links
8564         for ( iNode = 0; iNode < nbNodes; iNode++ )
8565           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8566                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8567               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8568           {
8569             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8570             curElemList.push_back( e );
8571           }
8572       }
8573     }
8574     // analyse the found
8575
8576     int nbNewBorders = curElemList.size();
8577     if ( nbNewBorders == 0 ) {
8578       // no free border furthermore
8579       return !needTheLast;
8580     }
8581     else if ( nbNewBorders == 1 ) {
8582       // one more element found
8583       nIgnore = nStart;
8584       nStart = nStartList.front();
8585       curElem = curElemList.front();
8586       theFaces.push_back( curElem );
8587       theNodes.push_back( nStart );
8588     }
8589     else {
8590       // several continuations found
8591       list< const SMDS_MeshElement* >::iterator curElemIt;
8592       list< const SMDS_MeshNode* >::iterator nStartIt;
8593       // check if one of them reached the last node
8594       if ( needTheLast ) {
8595         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8596              curElemIt!= curElemList.end();
8597              curElemIt++, nStartIt++ )
8598           if ( *nStartIt == theLastNode ) {
8599             theFaces.push_back( *curElemIt );
8600             theNodes.push_back( *nStartIt );
8601             return true;
8602           }
8603       }
8604       // find the best free border by the continuations
8605       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
8606       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8607       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8608            curElemIt!= curElemList.end();
8609            curElemIt++, nStartIt++ )
8610       {
8611         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8612         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8613         // find one more free border
8614         if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8615           cNL->clear();
8616           cFL->clear();
8617         }
8618         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8619           // choice: clear a worse one
8620           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8621           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8622           contNodes[ iWorse ].clear();
8623           contFaces[ iWorse ].clear();
8624         }
8625       }
8626       if ( contNodes[0].empty() && contNodes[1].empty() )
8627         return false;
8628
8629       // append the best free border
8630       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8631       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8632       theNodes.pop_back(); // remove nIgnore
8633       theNodes.pop_back(); // remove nStart
8634       theFaces.pop_back(); // remove curElem
8635       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8636       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8637       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8638       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8639       return true;
8640
8641     } // several continuations found
8642   } // while ( nStart != theLastNode )
8643
8644   return true;
8645 }
8646
8647 //=======================================================================
8648 //function : CheckFreeBorderNodes
8649 //purpose  : Return true if the tree nodes are on a free border
8650 //=======================================================================
8651
8652 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8653                                             const SMDS_MeshNode* theNode2,
8654                                             const SMDS_MeshNode* theNode3)
8655 {
8656   list< const SMDS_MeshNode* > nodes;
8657   list< const SMDS_MeshElement* > faces;
8658   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8659 }
8660
8661 //=======================================================================
8662 //function : SewFreeBorder
8663 //purpose  :
8664 //=======================================================================
8665
8666 SMESH_MeshEditor::Sew_Error
8667 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8668                                  const SMDS_MeshNode* theBordSecondNode,
8669                                  const SMDS_MeshNode* theBordLastNode,
8670                                  const SMDS_MeshNode* theSideFirstNode,
8671                                  const SMDS_MeshNode* theSideSecondNode,
8672                                  const SMDS_MeshNode* theSideThirdNode,
8673                                  const bool           theSideIsFreeBorder,
8674                                  const bool           toCreatePolygons,
8675                                  const bool           toCreatePolyedrs)
8676 {
8677   myLastCreatedElems.Clear();
8678   myLastCreatedNodes.Clear();
8679
8680   MESSAGE("::SewFreeBorder()");
8681   Sew_Error aResult = SEW_OK;
8682
8683   // ====================================
8684   //    find side nodes and elements
8685   // ====================================
8686
8687   list< const SMDS_MeshNode* > nSide[ 2 ];
8688   list< const SMDS_MeshElement* > eSide[ 2 ];
8689   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8690   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8691
8692   // Free border 1
8693   // --------------
8694   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8695                       nSide[0], eSide[0])) {
8696     MESSAGE(" Free Border 1 not found " );
8697     aResult = SEW_BORDER1_NOT_FOUND;
8698   }
8699   if (theSideIsFreeBorder) {
8700     // Free border 2
8701     // --------------
8702     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8703                         nSide[1], eSide[1])) {
8704       MESSAGE(" Free Border 2 not found " );
8705       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8706     }
8707   }
8708   if ( aResult != SEW_OK )
8709     return aResult;
8710
8711   if (!theSideIsFreeBorder) {
8712     // Side 2
8713     // --------------
8714
8715     // -------------------------------------------------------------------------
8716     // Algo:
8717     // 1. If nodes to merge are not coincident, move nodes of the free border
8718     //    from the coord sys defined by the direction from the first to last
8719     //    nodes of the border to the correspondent sys of the side 2
8720     // 2. On the side 2, find the links most co-directed with the correspondent
8721     //    links of the free border
8722     // -------------------------------------------------------------------------
8723
8724     // 1. Since sewing may break if there are volumes to split on the side 2,
8725     //    we wont move nodes but just compute new coordinates for them
8726     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8727     TNodeXYZMap nBordXYZ;
8728     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8729     list< const SMDS_MeshNode* >::iterator nBordIt;
8730
8731     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8732     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8733     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8734     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8735     double tol2 = 1.e-8;
8736     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8737     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8738       // Need node movement.
8739
8740       // find X and Z axes to create trsf
8741       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8742       gp_Vec X = Zs ^ Zb;
8743       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8744         // Zb || Zs
8745         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8746
8747       // coord systems
8748       gp_Ax3 toBordAx( Pb1, Zb, X );
8749       gp_Ax3 fromSideAx( Ps1, Zs, X );
8750       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8751       // set trsf
8752       gp_Trsf toBordSys, fromSide2Sys;
8753       toBordSys.SetTransformation( toBordAx );
8754       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8755       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8756
8757       // move
8758       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8759         const SMDS_MeshNode* n = *nBordIt;
8760         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8761         toBordSys.Transforms( xyz );
8762         fromSide2Sys.Transforms( xyz );
8763         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8764       }
8765     }
8766     else {
8767       // just insert nodes XYZ in the nBordXYZ map
8768       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8769         const SMDS_MeshNode* n = *nBordIt;
8770         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8771       }
8772     }
8773
8774     // 2. On the side 2, find the links most co-directed with the correspondent
8775     //    links of the free border
8776
8777     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8778     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8779     sideNodes.push_back( theSideFirstNode );
8780
8781     bool hasVolumes = false;
8782     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8783     set<long> foundSideLinkIDs, checkedLinkIDs;
8784     SMDS_VolumeTool volume;
8785     //const SMDS_MeshNode* faceNodes[ 4 ];
8786
8787     const SMDS_MeshNode*    sideNode;
8788     const SMDS_MeshElement* sideElem;
8789     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8790     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8791     nBordIt = bordNodes.begin();
8792     nBordIt++;
8793     // border node position and border link direction to compare with
8794     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8795     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8796     // choose next side node by link direction or by closeness to
8797     // the current border node:
8798     bool searchByDir = ( *nBordIt != theBordLastNode );
8799     do {
8800       // find the next node on the Side 2
8801       sideNode = 0;
8802       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8803       long linkID;
8804       checkedLinkIDs.clear();
8805       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8806
8807       // loop on inverse elements of current node (prevSideNode) on the Side 2
8808       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8809       while ( invElemIt->more() )
8810       {
8811         const SMDS_MeshElement* elem = invElemIt->next();
8812         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8813         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8814         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8815         bool isVolume = volume.Set( elem );
8816         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8817         if ( isVolume ) // --volume
8818           hasVolumes = true;
8819         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8820           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8821           if(elem->IsQuadratic()) {
8822             const SMDS_VtkFace* F =
8823               dynamic_cast<const SMDS_VtkFace*>(elem);
8824             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8825             // use special nodes iterator
8826             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8827             while( anIter->more() ) {
8828               nodes[ iNode ] = cast2Node(anIter->next());
8829               if ( nodes[ iNode++ ] == prevSideNode )
8830                 iPrevNode = iNode - 1;
8831             }
8832           }
8833           else {
8834             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8835             while ( nIt->more() ) {
8836               nodes[ iNode ] = cast2Node( nIt->next() );
8837               if ( nodes[ iNode++ ] == prevSideNode )
8838                 iPrevNode = iNode - 1;
8839             }
8840           }
8841           // there are 2 links to check
8842           nbNodes = 2;
8843         }
8844         else // --edge
8845           continue;
8846         // loop on links, to be precise, on the second node of links
8847         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8848           const SMDS_MeshNode* n = nodes[ iNode ];
8849           if ( isVolume ) {
8850             if ( !volume.IsLinked( n, prevSideNode ))
8851               continue;
8852           }
8853           else {
8854             if ( iNode ) // a node before prevSideNode
8855               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8856             else         // a node after prevSideNode
8857               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8858           }
8859           // check if this link was already used
8860           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8861           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8862           if (!isJustChecked &&
8863               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8864           {
8865             // test a link geometrically
8866             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8867             bool linkIsBetter = false;
8868             double dot = 0.0, dist = 0.0;
8869             if ( searchByDir ) { // choose most co-directed link
8870               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8871               linkIsBetter = ( dot > maxDot );
8872             }
8873             else { // choose link with the node closest to bordPos
8874               dist = ( nextXYZ - bordPos ).SquareModulus();
8875               linkIsBetter = ( dist < minDist );
8876             }
8877             if ( linkIsBetter ) {
8878               maxDot = dot;
8879               minDist = dist;
8880               linkID = iLink;
8881               sideNode = n;
8882               sideElem = elem;
8883             }
8884           }
8885         }
8886       } // loop on inverse elements of prevSideNode
8887
8888       if ( !sideNode ) {
8889         MESSAGE(" Cant find path by links of the Side 2 ");
8890         return SEW_BAD_SIDE_NODES;
8891       }
8892       sideNodes.push_back( sideNode );
8893       sideElems.push_back( sideElem );
8894       foundSideLinkIDs.insert ( linkID );
8895       prevSideNode = sideNode;
8896
8897       if ( *nBordIt == theBordLastNode )
8898         searchByDir = false;
8899       else {
8900         // find the next border link to compare with
8901         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8902         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8903         // move to next border node if sideNode is before forward border node (bordPos)
8904         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8905           prevBordNode = *nBordIt;
8906           nBordIt++;
8907           bordPos = nBordXYZ[ *nBordIt ];
8908           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8909           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8910         }
8911       }
8912     }
8913     while ( sideNode != theSideSecondNode );
8914
8915     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8916       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8917       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8918     }
8919   } // end nodes search on the side 2
8920
8921   // ============================
8922   // sew the border to the side 2
8923   // ============================
8924
8925   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8926   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8927
8928   TListOfListOfNodes nodeGroupsToMerge;
8929   if ( nbNodes[0] == nbNodes[1] ||
8930        ( theSideIsFreeBorder && !theSideThirdNode)) {
8931
8932     // all nodes are to be merged
8933
8934     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8935          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8936          nIt[0]++, nIt[1]++ )
8937     {
8938       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8939       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8940       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8941     }
8942   }
8943   else {
8944
8945     // insert new nodes into the border and the side to get equal nb of segments
8946
8947     // get normalized parameters of nodes on the borders
8948     //double param[ 2 ][ maxNbNodes ];
8949     double* param[ 2 ];
8950     param[0] = new double [ maxNbNodes ];
8951     param[1] = new double [ maxNbNodes ];
8952     int iNode, iBord;
8953     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8954       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8955       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8956       const SMDS_MeshNode* nPrev = *nIt;
8957       double bordLength = 0;
8958       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8959         const SMDS_MeshNode* nCur = *nIt;
8960         gp_XYZ segment (nCur->X() - nPrev->X(),
8961                         nCur->Y() - nPrev->Y(),
8962                         nCur->Z() - nPrev->Z());
8963         double segmentLen = segment.Modulus();
8964         bordLength += segmentLen;
8965         param[ iBord ][ iNode ] = bordLength;
8966         nPrev = nCur;
8967       }
8968       // normalize within [0,1]
8969       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8970         param[ iBord ][ iNode ] /= bordLength;
8971       }
8972     }
8973
8974     // loop on border segments
8975     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8976     int i[ 2 ] = { 0, 0 };
8977     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8978     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8979
8980     TElemOfNodeListMap insertMap;
8981     TElemOfNodeListMap::iterator insertMapIt;
8982     // insertMap is
8983     // key:   elem to insert nodes into
8984     // value: 2 nodes to insert between + nodes to be inserted
8985     do {
8986       bool next[ 2 ] = { false, false };
8987
8988       // find min adjacent segment length after sewing
8989       double nextParam = 10., prevParam = 0;
8990       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8991         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8992           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8993         if ( i[ iBord ] > 0 )
8994           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8995       }
8996       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8997       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8998       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8999
9000       // choose to insert or to merge nodes
9001       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
9002       if ( Abs( du ) <= minSegLen * 0.2 ) {
9003         // merge
9004         // ------
9005         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
9006         const SMDS_MeshNode* n0 = *nIt[0];
9007         const SMDS_MeshNode* n1 = *nIt[1];
9008         nodeGroupsToMerge.back().push_back( n1 );
9009         nodeGroupsToMerge.back().push_back( n0 );
9010         // position of node of the border changes due to merge
9011         param[ 0 ][ i[0] ] += du;
9012         // move n1 for the sake of elem shape evaluation during insertion.
9013         // n1 will be removed by MergeNodes() anyway
9014         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
9015         next[0] = next[1] = true;
9016       }
9017       else {
9018         // insert
9019         // ------
9020         int intoBord = ( du < 0 ) ? 0 : 1;
9021         const SMDS_MeshElement* elem = *eIt[ intoBord ];
9022         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
9023         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
9024         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
9025         if ( intoBord == 1 ) {
9026           // move node of the border to be on a link of elem of the side
9027           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
9028           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
9029           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
9030           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
9031           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
9032         }
9033         insertMapIt = insertMap.find( elem );
9034         bool notFound = ( insertMapIt == insertMap.end() );
9035         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
9036         if ( otherLink ) {
9037           // insert into another link of the same element:
9038           // 1. perform insertion into the other link of the elem
9039           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9040           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
9041           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
9042           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
9043           // 2. perform insertion into the link of adjacent faces
9044           while (true) {
9045             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
9046             if ( adjElem )
9047               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
9048             else
9049               break;
9050           }
9051           if (toCreatePolyedrs) {
9052             // perform insertion into the links of adjacent volumes
9053             UpdateVolumes(n12, n22, nodeList);
9054           }
9055           // 3. find an element appeared on n1 and n2 after the insertion
9056           insertMap.erase( elem );
9057           elem = findAdjacentFace( n1, n2, 0 );
9058         }
9059         if ( notFound || otherLink ) {
9060           // add element and nodes of the side into the insertMap
9061           insertMapIt = insertMap.insert
9062             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
9063           (*insertMapIt).second.push_back( n1 );
9064           (*insertMapIt).second.push_back( n2 );
9065         }
9066         // add node to be inserted into elem
9067         (*insertMapIt).second.push_back( nIns );
9068         next[ 1 - intoBord ] = true;
9069       }
9070
9071       // go to the next segment
9072       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
9073         if ( next[ iBord ] ) {
9074           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
9075             eIt[ iBord ]++;
9076           nPrev[ iBord ] = *nIt[ iBord ];
9077           nIt[ iBord ]++; i[ iBord ]++;
9078         }
9079       }
9080     }
9081     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
9082
9083     // perform insertion of nodes into elements
9084
9085     for (insertMapIt = insertMap.begin();
9086          insertMapIt != insertMap.end();
9087          insertMapIt++ )
9088     {
9089       const SMDS_MeshElement* elem = (*insertMapIt).first;
9090       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
9091       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
9092       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
9093
9094       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
9095
9096       if ( !theSideIsFreeBorder ) {
9097         // look for and insert nodes into the faces adjacent to elem
9098         while (true) {
9099           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
9100           if ( adjElem )
9101             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
9102           else
9103             break;
9104         }
9105       }
9106       if (toCreatePolyedrs) {
9107         // perform insertion into the links of adjacent volumes
9108         UpdateVolumes(n1, n2, nodeList);
9109       }
9110     }
9111
9112     delete param[0];
9113     delete param[1];
9114   } // end: insert new nodes
9115
9116   MergeNodes ( nodeGroupsToMerge );
9117
9118   return aResult;
9119 }
9120
9121 //=======================================================================
9122 //function : InsertNodesIntoLink
9123 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
9124 //           and theBetweenNode2 and split theElement
9125 //=======================================================================
9126
9127 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
9128                                            const SMDS_MeshNode*        theBetweenNode1,
9129                                            const SMDS_MeshNode*        theBetweenNode2,
9130                                            list<const SMDS_MeshNode*>& theNodesToInsert,
9131                                            const bool                  toCreatePoly)
9132 {
9133   if ( theFace->GetType() != SMDSAbs_Face ) return;
9134
9135   // find indices of 2 link nodes and of the rest nodes
9136   int iNode = 0, il1, il2, i3, i4;
9137   il1 = il2 = i3 = i4 = -1;
9138   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
9139   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
9140
9141   if(theFace->IsQuadratic()) {
9142     const SMDS_VtkFace* F =
9143       dynamic_cast<const SMDS_VtkFace*>(theFace);
9144     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9145     // use special nodes iterator
9146     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9147     while( anIter->more() ) {
9148       const SMDS_MeshNode* n = cast2Node(anIter->next());
9149       if ( n == theBetweenNode1 )
9150         il1 = iNode;
9151       else if ( n == theBetweenNode2 )
9152         il2 = iNode;
9153       else if ( i3 < 0 )
9154         i3 = iNode;
9155       else
9156         i4 = iNode;
9157       nodes[ iNode++ ] = n;
9158     }
9159   }
9160   else {
9161     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9162     while ( nodeIt->more() ) {
9163       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9164       if ( n == theBetweenNode1 )
9165         il1 = iNode;
9166       else if ( n == theBetweenNode2 )
9167         il2 = iNode;
9168       else if ( i3 < 0 )
9169         i3 = iNode;
9170       else
9171         i4 = iNode;
9172       nodes[ iNode++ ] = n;
9173     }
9174   }
9175   if ( il1 < 0 || il2 < 0 || i3 < 0 )
9176     return ;
9177
9178   // arrange link nodes to go one after another regarding the face orientation
9179   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9180   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9181   if ( reverse ) {
9182     iNode = il1;
9183     il1 = il2;
9184     il2 = iNode;
9185     aNodesToInsert.reverse();
9186   }
9187   // check that not link nodes of a quadrangles are in good order
9188   int nbFaceNodes = theFace->NbNodes();
9189   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9190     iNode = i3;
9191     i3 = i4;
9192     i4 = iNode;
9193   }
9194
9195   if (toCreatePoly || theFace->IsPoly()) {
9196
9197     iNode = 0;
9198     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9199
9200     // add nodes of face up to first node of link
9201     bool isFLN = false;
9202
9203     if(theFace->IsQuadratic()) {
9204       const SMDS_VtkFace* F =
9205         dynamic_cast<const SMDS_VtkFace*>(theFace);
9206       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9207       // use special nodes iterator
9208       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9209       while( anIter->more()  && !isFLN ) {
9210         const SMDS_MeshNode* n = cast2Node(anIter->next());
9211         poly_nodes[iNode++] = n;
9212         if (n == nodes[il1]) {
9213           isFLN = true;
9214         }
9215       }
9216       // add nodes to insert
9217       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9218       for (; nIt != aNodesToInsert.end(); nIt++) {
9219         poly_nodes[iNode++] = *nIt;
9220       }
9221       // add nodes of face starting from last node of link
9222       while ( anIter->more() ) {
9223         poly_nodes[iNode++] = cast2Node(anIter->next());
9224       }
9225     }
9226     else {
9227       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9228       while ( nodeIt->more() && !isFLN ) {
9229         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9230         poly_nodes[iNode++] = n;
9231         if (n == nodes[il1]) {
9232           isFLN = true;
9233         }
9234       }
9235       // add nodes to insert
9236       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9237       for (; nIt != aNodesToInsert.end(); nIt++) {
9238         poly_nodes[iNode++] = *nIt;
9239       }
9240       // add nodes of face starting from last node of link
9241       while ( nodeIt->more() ) {
9242         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9243         poly_nodes[iNode++] = n;
9244       }
9245     }
9246
9247     // edit or replace the face
9248     SMESHDS_Mesh *aMesh = GetMeshDS();
9249
9250     if (theFace->IsPoly()) {
9251       aMesh->ChangePolygonNodes(theFace, poly_nodes);
9252     }
9253     else {
9254       int aShapeId = FindShape( theFace );
9255
9256       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9257       myLastCreatedElems.Append(newElem);
9258       if ( aShapeId && newElem )
9259         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9260
9261       aMesh->RemoveElement(theFace);
9262     }
9263     return;
9264   }
9265
9266   SMESHDS_Mesh *aMesh = GetMeshDS();
9267   if( !theFace->IsQuadratic() ) {
9268
9269     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9270     int nbLinkNodes = 2 + aNodesToInsert.size();
9271     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9272     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9273     linkNodes[ 0 ] = nodes[ il1 ];
9274     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9275     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9276     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9277       linkNodes[ iNode++ ] = *nIt;
9278     }
9279     // decide how to split a quadrangle: compare possible variants
9280     // and choose which of splits to be a quadrangle
9281     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9282     if ( nbFaceNodes == 3 ) {
9283       iBestQuad = nbSplits;
9284       i4 = i3;
9285     }
9286     else if ( nbFaceNodes == 4 ) {
9287       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9288       double aBestRate = DBL_MAX;
9289       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9290         i1 = 0; i2 = 1;
9291         double aBadRate = 0;
9292         // evaluate elements quality
9293         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9294           if ( iSplit == iQuad ) {
9295             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9296                                    linkNodes[ i2++ ],
9297                                    nodes[ i3 ],
9298                                    nodes[ i4 ]);
9299             aBadRate += getBadRate( &quad, aCrit );
9300           }
9301           else {
9302             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9303                                    linkNodes[ i2++ ],
9304                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
9305             aBadRate += getBadRate( &tria, aCrit );
9306           }
9307         }
9308         // choice
9309         if ( aBadRate < aBestRate ) {
9310           iBestQuad = iQuad;
9311           aBestRate = aBadRate;
9312         }
9313       }
9314     }
9315
9316     // create new elements
9317     int aShapeId = FindShape( theFace );
9318
9319     i1 = 0; i2 = 1;
9320     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9321       SMDS_MeshElement* newElem = 0;
9322       if ( iSplit == iBestQuad )
9323         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9324                                   linkNodes[ i2++ ],
9325                                   nodes[ i3 ],
9326                                   nodes[ i4 ]);
9327       else
9328         newElem = aMesh->AddFace (linkNodes[ i1++ ],
9329                                   linkNodes[ i2++ ],
9330                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9331       myLastCreatedElems.Append(newElem);
9332       if ( aShapeId && newElem )
9333         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9334     }
9335
9336     // change nodes of theFace
9337     const SMDS_MeshNode* newNodes[ 4 ];
9338     newNodes[ 0 ] = linkNodes[ i1 ];
9339     newNodes[ 1 ] = linkNodes[ i2 ];
9340     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9341     newNodes[ 3 ] = nodes[ i4 ];
9342     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9343     const SMDS_MeshElement* newElem = 0;
9344     if (iSplit == iBestQuad)
9345       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9346     else
9347       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9348     myLastCreatedElems.Append(newElem);
9349     if ( aShapeId && newElem )
9350       aMesh->SetMeshElementOnShape( newElem, aShapeId );
9351 } // end if(!theFace->IsQuadratic())
9352   else { // theFace is quadratic
9353     // we have to split theFace on simple triangles and one simple quadrangle
9354     int tmp = il1/2;
9355     int nbshift = tmp*2;
9356     // shift nodes in nodes[] by nbshift
9357     int i,j;
9358     for(i=0; i<nbshift; i++) {
9359       const SMDS_MeshNode* n = nodes[0];
9360       for(j=0; j<nbFaceNodes-1; j++) {
9361         nodes[j] = nodes[j+1];
9362       }
9363       nodes[nbFaceNodes-1] = n;
9364     }
9365     il1 = il1 - nbshift;
9366     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9367     //   n0      n1     n2    n0      n1     n2
9368     //     +-----+-----+        +-----+-----+
9369     //      \         /         |           |
9370     //       \       /          |           |
9371     //      n5+     +n3       n7+           +n3
9372     //         \   /            |           |
9373     //          \ /             |           |
9374     //           +              +-----+-----+
9375     //           n4           n6      n5     n4
9376
9377     // create new elements
9378     int aShapeId = FindShape( theFace );
9379
9380     int n1,n2,n3;
9381     if(nbFaceNodes==6) { // quadratic triangle
9382       SMDS_MeshElement* newElem =
9383         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9384       myLastCreatedElems.Append(newElem);
9385       if ( aShapeId && newElem )
9386         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9387       if(theFace->IsMediumNode(nodes[il1])) {
9388         // create quadrangle
9389         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9390         myLastCreatedElems.Append(newElem);
9391         if ( aShapeId && newElem )
9392           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9393         n1 = 1;
9394         n2 = 2;
9395         n3 = 3;
9396       }
9397       else {
9398         // create quadrangle
9399         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9400         myLastCreatedElems.Append(newElem);
9401         if ( aShapeId && newElem )
9402           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9403         n1 = 0;
9404         n2 = 1;
9405         n3 = 5;
9406       }
9407     }
9408     else { // nbFaceNodes==8 - quadratic quadrangle
9409       SMDS_MeshElement* newElem =
9410         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9411       myLastCreatedElems.Append(newElem);
9412       if ( aShapeId && newElem )
9413         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9414       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9415       myLastCreatedElems.Append(newElem);
9416       if ( aShapeId && newElem )
9417         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9418       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9419       myLastCreatedElems.Append(newElem);
9420       if ( aShapeId && newElem )
9421         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9422       if(theFace->IsMediumNode(nodes[il1])) {
9423         // create quadrangle
9424         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9425         myLastCreatedElems.Append(newElem);
9426         if ( aShapeId && newElem )
9427           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9428         n1 = 1;
9429         n2 = 2;
9430         n3 = 3;
9431       }
9432       else {
9433         // create quadrangle
9434         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9435         myLastCreatedElems.Append(newElem);
9436         if ( aShapeId && newElem )
9437           aMesh->SetMeshElementOnShape( newElem, aShapeId );
9438         n1 = 0;
9439         n2 = 1;
9440         n3 = 7;
9441       }
9442     }
9443     // create needed triangles using n1,n2,n3 and inserted nodes
9444     int nbn = 2 + aNodesToInsert.size();
9445     //const SMDS_MeshNode* aNodes[nbn];
9446     vector<const SMDS_MeshNode*> aNodes(nbn);
9447     aNodes[0] = nodes[n1];
9448     aNodes[nbn-1] = nodes[n2];
9449     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9450     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9451       aNodes[iNode++] = *nIt;
9452     }
9453     for(i=1; i<nbn; i++) {
9454       SMDS_MeshElement* newElem =
9455         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9456       myLastCreatedElems.Append(newElem);
9457       if ( aShapeId && newElem )
9458         aMesh->SetMeshElementOnShape( newElem, aShapeId );
9459     }
9460   }
9461   // remove old face
9462   aMesh->RemoveElement(theFace);
9463 }
9464
9465 //=======================================================================
9466 //function : UpdateVolumes
9467 //purpose  :
9468 //=======================================================================
9469 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
9470                                       const SMDS_MeshNode*        theBetweenNode2,
9471                                       list<const SMDS_MeshNode*>& theNodesToInsert)
9472 {
9473   myLastCreatedElems.Clear();
9474   myLastCreatedNodes.Clear();
9475
9476   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9477   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9478     const SMDS_MeshElement* elem = invElemIt->next();
9479
9480     // check, if current volume has link theBetweenNode1 - theBetweenNode2
9481     SMDS_VolumeTool aVolume (elem);
9482     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9483       continue;
9484
9485     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9486     int iface, nbFaces = aVolume.NbFaces();
9487     vector<const SMDS_MeshNode *> poly_nodes;
9488     vector<int> quantities (nbFaces);
9489
9490     for (iface = 0; iface < nbFaces; iface++) {
9491       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9492       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9493       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9494
9495       for (int inode = 0; inode < nbFaceNodes; inode++) {
9496         poly_nodes.push_back(faceNodes[inode]);
9497
9498         if (nbInserted == 0) {
9499           if (faceNodes[inode] == theBetweenNode1) {
9500             if (faceNodes[inode + 1] == theBetweenNode2) {
9501               nbInserted = theNodesToInsert.size();
9502
9503               // add nodes to insert
9504               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9505               for (; nIt != theNodesToInsert.end(); nIt++) {
9506                 poly_nodes.push_back(*nIt);
9507               }
9508             }
9509           }
9510           else if (faceNodes[inode] == theBetweenNode2) {
9511             if (faceNodes[inode + 1] == theBetweenNode1) {
9512               nbInserted = theNodesToInsert.size();
9513
9514               // add nodes to insert in reversed order
9515               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9516               nIt--;
9517               for (; nIt != theNodesToInsert.begin(); nIt--) {
9518                 poly_nodes.push_back(*nIt);
9519               }
9520               poly_nodes.push_back(*nIt);
9521             }
9522           }
9523           else {
9524           }
9525         }
9526       }
9527       quantities[iface] = nbFaceNodes + nbInserted;
9528     }
9529
9530     // Replace or update the volume
9531     SMESHDS_Mesh *aMesh = GetMeshDS();
9532
9533     if (elem->IsPoly()) {
9534       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9535
9536     }
9537     else {
9538       int aShapeId = FindShape( elem );
9539
9540       SMDS_MeshElement* newElem =
9541         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9542       myLastCreatedElems.Append(newElem);
9543       if (aShapeId && newElem)
9544         aMesh->SetMeshElementOnShape(newElem, aShapeId);
9545
9546       aMesh->RemoveElement(elem);
9547     }
9548   }
9549 }
9550
9551 namespace
9552 {
9553   //================================================================================
9554   /*!
9555    * \brief Transform any volume into data of SMDSEntity_Polyhedra
9556    */
9557   //================================================================================
9558
9559   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
9560                            vector<const SMDS_MeshNode *> & nodes,
9561                            vector<int> &                   nbNodeInFaces )
9562   {
9563     nodes.clear();
9564     nbNodeInFaces.clear();
9565     SMDS_VolumeTool vTool ( elem );
9566     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9567     {
9568       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9569       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9570       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9571     }
9572   }
9573 }
9574
9575 //=======================================================================
9576 /*!
9577  * \brief Convert elements contained in a submesh to quadratic
9578  * \return int - nb of checked elements
9579  */
9580 //=======================================================================
9581
9582 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
9583                                              SMESH_MesherHelper& theHelper,
9584                                              const bool          theForce3d)
9585 {
9586   int nbElem = 0;
9587   if( !theSm ) return nbElem;
9588
9589   vector<int> nbNodeInFaces;
9590   vector<const SMDS_MeshNode *> nodes;
9591   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9592   while(ElemItr->more())
9593   {
9594     nbElem++;
9595     const SMDS_MeshElement* elem = ElemItr->next();
9596     if( !elem ) continue;
9597
9598     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9599     if ( elem->IsQuadratic() )
9600     {
9601       bool alreadyOK;
9602       switch ( aGeomType ) {
9603       case SMDSEntity_Quad_Quadrangle:
9604       case SMDSEntity_Quad_Hexa:         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9605       case SMDSEntity_BiQuad_Quadrangle:
9606       case SMDSEntity_TriQuad_Hexa:      alreadyOK = theHelper.GetIsBiQuadratic(); break;
9607       default:                           alreadyOK = true;
9608       }
9609       if ( alreadyOK ) continue;
9610     }
9611     // get elem data needed to re-create it
9612     //
9613     const int id                     = elem->GetID();
9614     const int nbNodes                = elem->NbCornerNodes();
9615     const SMDSAbs_ElementType aType  = elem->GetType();
9616     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9617     if ( aGeomType == SMDSEntity_Polyhedra )
9618       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9619     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9620       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9621
9622     // remove a linear element
9623     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9624
9625     const SMDS_MeshElement* NewElem = 0;
9626
9627     switch( aType )
9628     {
9629     case SMDSAbs_Edge :
9630       {
9631         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9632         break;
9633       }
9634     case SMDSAbs_Face :
9635       {
9636         switch(nbNodes)
9637         {
9638         case 3:
9639           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9640           break;
9641         case 4:
9642           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9643           break;
9644         default:
9645           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9646           continue;
9647         }
9648         break;
9649       }
9650     case SMDSAbs_Volume :
9651       {
9652         switch( aGeomType )
9653         {
9654         case SMDSEntity_Tetra:
9655           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9656           break;
9657         case SMDSEntity_Pyramid:
9658           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9659           break;
9660         case SMDSEntity_Penta:
9661           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9662           break;
9663         case SMDSEntity_Hexa:
9664         case SMDSEntity_Quad_Hexa:
9665         case SMDSEntity_TriQuad_Hexa:
9666           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9667                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9668           break;
9669         case SMDSEntity_Hexagonal_Prism:
9670         default:
9671           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9672         }
9673         break;
9674       }
9675     default :
9676       continue;
9677     }
9678     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9679     if( NewElem )
9680       theSm->AddElement( NewElem );
9681   }
9682   return nbElem;
9683 }
9684 //=======================================================================
9685 //function : ConvertToQuadratic
9686 //purpose  :
9687 //=======================================================================
9688
9689 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9690 {
9691   SMESHDS_Mesh* meshDS = GetMeshDS();
9692
9693   SMESH_MesherHelper aHelper(*myMesh);
9694
9695   aHelper.SetIsQuadratic( true );
9696   aHelper.SetIsBiQuadratic( theToBiQuad );
9697   aHelper.SetElementsOnShape(true);
9698
9699   int nbCheckedElems = 0;
9700   if ( myMesh->HasShapeToMesh() )
9701   {
9702     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9703     {
9704       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9705       while ( smIt->more() ) {
9706         SMESH_subMesh* sm = smIt->next();
9707         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9708           aHelper.SetSubShape( sm->GetSubShape() );
9709           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9710         }
9711       }
9712     }
9713   }
9714   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9715   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9716   {
9717     aHelper.SetElementsOnShape(false);
9718     SMESHDS_SubMesh *smDS = 0;
9719     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9720     while(aEdgeItr->more())
9721     {
9722       const SMDS_MeshEdge* edge = aEdgeItr->next();
9723       if(edge && !edge->IsQuadratic())
9724       {
9725         int id = edge->GetID();
9726         //MESSAGE("edge->GetID() " << id);
9727         const SMDS_MeshNode* n1 = edge->GetNode(0);
9728         const SMDS_MeshNode* n2 = edge->GetNode(1);
9729
9730         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9731
9732         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9733         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9734       }
9735     }
9736     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9737     while(aFaceItr->more())
9738     {
9739       const SMDS_MeshFace* face = aFaceItr->next();
9740       if ( !face ) continue;
9741       
9742       const SMDSAbs_EntityType type = face->GetEntityType();
9743       if (( theToBiQuad  && type == SMDSEntity_BiQuad_Quadrangle ) ||
9744           ( !theToBiQuad && type == SMDSEntity_Quad_Quadrangle ))
9745         continue;
9746
9747       const int id = face->GetID();
9748       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9749
9750       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9751
9752       SMDS_MeshFace * NewFace = 0;
9753       switch( type )
9754       {
9755       case SMDSEntity_Triangle:
9756         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9757         break;
9758       case SMDSEntity_Quadrangle:
9759         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9760         break;
9761       default:
9762         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9763       }
9764       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9765     }
9766     vector<int> nbNodeInFaces;
9767     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9768     while(aVolumeItr->more())
9769     {
9770       const SMDS_MeshVolume* volume = aVolumeItr->next();
9771       if(!volume || volume->IsQuadratic() ) continue;
9772
9773       const SMDSAbs_EntityType type = volume->GetEntityType();
9774       if (( theToBiQuad  && type == SMDSEntity_TriQuad_Hexa ) ||
9775           ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
9776         continue;
9777
9778       const int id = volume->GetID();
9779       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9780       if ( type == SMDSEntity_Polyhedra )
9781         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9782       else if ( type == SMDSEntity_Hexagonal_Prism )
9783         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9784
9785       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9786
9787       SMDS_MeshVolume * NewVolume = 0;
9788       switch ( type )
9789       {
9790       case SMDSEntity_Tetra:
9791         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9792         break;
9793       case SMDSEntity_Hexa:
9794       case SMDSEntity_Quad_Hexa:
9795       case SMDSEntity_TriQuad_Hexa:
9796         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9797                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9798         break;
9799       case SMDSEntity_Pyramid:
9800         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9801                                       nodes[3], nodes[4], id, theForce3d);
9802         break;
9803       case SMDSEntity_Penta:
9804         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9805                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9806         break;
9807       case SMDSEntity_Hexagonal_Prism:
9808       default:
9809         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9810       }
9811       ReplaceElemInGroups(volume, NewVolume, meshDS);
9812     }
9813   }
9814
9815   if ( !theForce3d )
9816   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9817     aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9818     aHelper.FixQuadraticElements(myError);
9819   }
9820 }
9821
9822 //================================================================================
9823 /*!
9824  * \brief Makes given elements quadratic
9825  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9826  *  \param theElements - elements to make quadratic
9827  */
9828 //================================================================================
9829
9830 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9831                                           TIDSortedElemSet& theElements,
9832                                           const bool        theToBiQuad)
9833 {
9834   if ( theElements.empty() ) return;
9835
9836   // we believe that all theElements are of the same type
9837   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9838
9839   // get all nodes shared by theElements
9840   TIDSortedNodeSet allNodes;
9841   TIDSortedElemSet::iterator eIt = theElements.begin();
9842   for ( ; eIt != theElements.end(); ++eIt )
9843     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9844
9845   // complete theElements with elements of lower dim whose all nodes are in allNodes
9846
9847   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9848   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9849   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9850   for ( ; nIt != allNodes.end(); ++nIt )
9851   {
9852     const SMDS_MeshNode* n = *nIt;
9853     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9854     while ( invIt->more() )
9855     {
9856       const SMDS_MeshElement* e = invIt->next();
9857       if ( e->IsQuadratic() )
9858       {
9859         bool alreadyOK;
9860         switch ( e->GetEntityType() ) {
9861         case SMDSEntity_Quad_Quadrangle:
9862         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9863         case SMDSEntity_BiQuad_Quadrangle:
9864         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9865         default:                           alreadyOK = true;
9866         }
9867         if ( alreadyOK )
9868         {
9869           quadAdjacentElems[ e->GetType() ].insert( e );
9870           continue;
9871         }
9872       }
9873       if ( e->GetType() >= elemType )
9874       {
9875         continue; // same type of more complex linear element
9876       }
9877
9878       if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9879         continue; // e is already checked
9880
9881       // check nodes
9882       bool allIn = true;
9883       SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9884       while ( nodeIt->more() && allIn )
9885         allIn = allNodes.count( cast2Node( nodeIt->next() ));
9886       if ( allIn )
9887         theElements.insert(e );
9888     }
9889   }
9890
9891   SMESH_MesherHelper helper(*myMesh);
9892   helper.SetIsQuadratic( true );
9893   helper.SetIsBiQuadratic( theToBiQuad );
9894
9895   // add links of quadratic adjacent elements to the helper
9896
9897   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9898     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9899           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9900     {
9901       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9902     }
9903   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9904     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9905           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9906     {
9907       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9908     }
9909   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9910     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9911           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9912     {
9913       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9914     }
9915
9916   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9917
9918   SMESHDS_Mesh*  meshDS = GetMeshDS();
9919   SMESHDS_SubMesh* smDS = 0;
9920   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9921   {
9922     const SMDS_MeshElement* elem = *eIt;
9923     if( elem->NbNodes() < 2 || elem->IsPoly() )
9924       continue;
9925
9926     if ( elem->IsQuadratic() )
9927     {
9928       bool alreadyOK;
9929       switch ( elem->GetEntityType() ) {
9930       case SMDSEntity_Quad_Quadrangle:
9931       case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9932       case SMDSEntity_BiQuad_Quadrangle:
9933       case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9934       default:                           alreadyOK = true;
9935       }
9936       if ( alreadyOK ) continue;
9937     }
9938
9939     const SMDSAbs_ElementType type = elem->GetType();
9940     const int                   id = elem->GetID();
9941     const int              nbNodes = elem->NbCornerNodes();
9942     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9943
9944     if ( !smDS || !smDS->Contains( elem ))
9945       smDS = meshDS->MeshElements( elem->getshapeId() );
9946     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9947
9948     SMDS_MeshElement * newElem = 0;
9949     switch( nbNodes )
9950     {
9951     case 4: // cases for most frequently used element types go first (for optimization)
9952       if ( type == SMDSAbs_Volume )
9953         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9954       else
9955         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9956       break;
9957     case 8:
9958       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9959                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9960       break;
9961     case 3:
9962       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9963       break;
9964     case 2:
9965       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9966       break;
9967     case 5:
9968       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9969                                  nodes[4], id, theForce3d);
9970       break;
9971     case 6:
9972       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9973                                  nodes[4], nodes[5], id, theForce3d);
9974       break;
9975     default:;
9976     }
9977     ReplaceElemInGroups( elem, newElem, meshDS);
9978     if( newElem && smDS )
9979       smDS->AddElement( newElem );
9980   }
9981
9982   if ( !theForce3d  && !getenv("NO_FixQuadraticElements"))
9983   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9984     helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9985     helper.FixQuadraticElements( myError );
9986   }
9987 }
9988
9989 //=======================================================================
9990 /*!
9991  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9992  * \return int - nb of checked elements
9993  */
9994 //=======================================================================
9995
9996 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9997                                      SMDS_ElemIteratorPtr theItr,
9998                                      const int            theShapeID)
9999 {
10000   int nbElem = 0;
10001   SMESHDS_Mesh* meshDS = GetMeshDS();
10002
10003   while( theItr->more() )
10004   {
10005     const SMDS_MeshElement* elem = theItr->next();
10006     nbElem++;
10007     if( elem && elem->IsQuadratic())
10008     {
10009       int id                    = elem->GetID();
10010       int nbCornerNodes         = elem->NbCornerNodes();
10011       SMDSAbs_ElementType aType = elem->GetType();
10012
10013       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
10014
10015       //remove a quadratic element
10016       if ( !theSm || !theSm->Contains( elem ))
10017         theSm = meshDS->MeshElements( elem->getshapeId() );
10018       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
10019
10020       // remove medium nodes
10021       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
10022         if ( nodes[i]->NbInverseElements() == 0 )
10023           meshDS->RemoveFreeNode( nodes[i], theSm );
10024
10025       // add a linear element
10026       nodes.resize( nbCornerNodes );
10027       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
10028       ReplaceElemInGroups(elem, newElem, meshDS);
10029       if( theSm && newElem )
10030         theSm->AddElement( newElem );
10031     }
10032   }
10033   return nbElem;
10034 }
10035
10036 //=======================================================================
10037 //function : ConvertFromQuadratic
10038 //purpose  :
10039 //=======================================================================
10040
10041 bool SMESH_MeshEditor::ConvertFromQuadratic()
10042 {
10043   int nbCheckedElems = 0;
10044   if ( myMesh->HasShapeToMesh() )
10045   {
10046     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
10047     {
10048       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
10049       while ( smIt->more() ) {
10050         SMESH_subMesh* sm = smIt->next();
10051         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
10052           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
10053       }
10054     }
10055   }
10056
10057   int totalNbElems =
10058     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
10059   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
10060   {
10061     SMESHDS_SubMesh *aSM = 0;
10062     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
10063   }
10064
10065   return true;
10066 }
10067
10068 namespace
10069 {
10070   //================================================================================
10071   /*!
10072    * \brief Return true if all medium nodes of the element are in the node set
10073    */
10074   //================================================================================
10075
10076   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
10077   {
10078     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
10079       if ( !nodeSet.count( elem->GetNode(i) ))
10080         return false;
10081     return true;
10082   }
10083 }
10084
10085 //================================================================================
10086 /*!
10087  * \brief Makes given elements linear
10088  */
10089 //================================================================================
10090
10091 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
10092 {
10093   if ( theElements.empty() ) return;
10094
10095   // collect IDs of medium nodes of theElements; some of these nodes will be removed
10096   set<int> mediumNodeIDs;
10097   TIDSortedElemSet::iterator eIt = theElements.begin();
10098   for ( ; eIt != theElements.end(); ++eIt )
10099   {
10100     const SMDS_MeshElement* e = *eIt;
10101     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
10102       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
10103   }
10104
10105   // replace given elements by linear ones
10106   typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
10107   SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
10108   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10109
10110   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
10111   // except those elements sharing medium nodes of quadratic element whose medium nodes
10112   // are not all in mediumNodeIDs
10113
10114   // get remaining medium nodes
10115   TIDSortedNodeSet mediumNodes;
10116   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
10117   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
10118     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
10119       mediumNodes.insert( mediumNodes.end(), n );
10120
10121   // find more quadratic elements to convert
10122   TIDSortedElemSet moreElemsToConvert;
10123   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
10124   for ( ; nIt != mediumNodes.end(); ++nIt )
10125   {
10126     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
10127     while ( invIt->more() )
10128     {
10129       const SMDS_MeshElement* e = invIt->next();
10130       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
10131       {
10132         // find a more complex element including e and
10133         // whose medium nodes are not in mediumNodes
10134         bool complexFound = false;
10135         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
10136         {
10137           SMDS_ElemIteratorPtr invIt2 =
10138             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
10139           while ( invIt2->more() )
10140           {
10141             const SMDS_MeshElement* eComplex = invIt2->next();
10142             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
10143             {
10144               int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
10145               if ( nbCommonNodes == e->NbNodes())
10146               {
10147                 complexFound = true;
10148                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
10149                 break;
10150               }
10151             }
10152           }
10153         }
10154         if ( !complexFound )
10155           moreElemsToConvert.insert( e );
10156       }
10157     }
10158   }
10159   elemIt = SMDS_ElemIteratorPtr
10160     (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
10161   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
10162 }
10163
10164 //=======================================================================
10165 //function : SewSideElements
10166 //purpose  :
10167 //=======================================================================
10168
10169 SMESH_MeshEditor::Sew_Error
10170 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
10171                                    TIDSortedElemSet&    theSide2,
10172                                    const SMDS_MeshNode* theFirstNode1,
10173                                    const SMDS_MeshNode* theFirstNode2,
10174                                    const SMDS_MeshNode* theSecondNode1,
10175                                    const SMDS_MeshNode* theSecondNode2)
10176 {
10177   myLastCreatedElems.Clear();
10178   myLastCreatedNodes.Clear();
10179
10180   MESSAGE ("::::SewSideElements()");
10181   if ( theSide1.size() != theSide2.size() )
10182     return SEW_DIFF_NB_OF_ELEMENTS;
10183
10184   Sew_Error aResult = SEW_OK;
10185   // Algo:
10186   // 1. Build set of faces representing each side
10187   // 2. Find which nodes of the side 1 to merge with ones on the side 2
10188   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10189
10190   // =======================================================================
10191   // 1. Build set of faces representing each side:
10192   // =======================================================================
10193   // a. build set of nodes belonging to faces
10194   // b. complete set of faces: find missing faces whose nodes are in set of nodes
10195   // c. create temporary faces representing side of volumes if correspondent
10196   //    face does not exist
10197
10198   SMESHDS_Mesh* aMesh = GetMeshDS();
10199   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
10200   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
10201   TIDSortedElemSet             faceSet1, faceSet2;
10202   set<const SMDS_MeshElement*> volSet1,  volSet2;
10203   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
10204   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
10205   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
10206   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10207   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
10208   int iSide, iFace, iNode;
10209
10210   list<const SMDS_MeshElement* > tempFaceList;
10211   for ( iSide = 0; iSide < 2; iSide++ ) {
10212     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
10213     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
10214     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
10215     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
10216     set<const SMDS_MeshElement*>::iterator vIt;
10217     TIDSortedElemSet::iterator eIt;
10218     set<const SMDS_MeshNode*>::iterator    nIt;
10219
10220     // check that given nodes belong to given elements
10221     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10222     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10223     int firstIndex = -1, secondIndex = -1;
10224     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10225       const SMDS_MeshElement* elem = *eIt;
10226       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
10227       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10228       if ( firstIndex > -1 && secondIndex > -1 ) break;
10229     }
10230     if ( firstIndex < 0 || secondIndex < 0 ) {
10231       // we can simply return until temporary faces created
10232       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10233     }
10234
10235     // -----------------------------------------------------------
10236     // 1a. Collect nodes of existing faces
10237     //     and build set of face nodes in order to detect missing
10238     //     faces corresponding to sides of volumes
10239     // -----------------------------------------------------------
10240
10241     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10242
10243     // loop on the given element of a side
10244     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10245       //const SMDS_MeshElement* elem = *eIt;
10246       const SMDS_MeshElement* elem = *eIt;
10247       if ( elem->GetType() == SMDSAbs_Face ) {
10248         faceSet->insert( elem );
10249         set <const SMDS_MeshNode*> faceNodeSet;
10250         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10251         while ( nodeIt->more() ) {
10252           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10253           nodeSet->insert( n );
10254           faceNodeSet.insert( n );
10255         }
10256         setOfFaceNodeSet.insert( faceNodeSet );
10257       }
10258       else if ( elem->GetType() == SMDSAbs_Volume )
10259         volSet->insert( elem );
10260     }
10261     // ------------------------------------------------------------------------------
10262     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10263     // ------------------------------------------------------------------------------
10264
10265     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10266       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10267       while ( fIt->more() ) { // loop on faces sharing a node
10268         const SMDS_MeshElement* f = fIt->next();
10269         if ( faceSet->find( f ) == faceSet->end() ) {
10270           // check if all nodes are in nodeSet and
10271           // complete setOfFaceNodeSet if they are
10272           set <const SMDS_MeshNode*> faceNodeSet;
10273           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10274           bool allInSet = true;
10275           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10276             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10277             if ( nodeSet->find( n ) == nodeSet->end() )
10278               allInSet = false;
10279             else
10280               faceNodeSet.insert( n );
10281           }
10282           if ( allInSet ) {
10283             faceSet->insert( f );
10284             setOfFaceNodeSet.insert( faceNodeSet );
10285           }
10286         }
10287       }
10288     }
10289
10290     // -------------------------------------------------------------------------
10291     // 1c. Create temporary faces representing sides of volumes if correspondent
10292     //     face does not exist
10293     // -------------------------------------------------------------------------
10294
10295     if ( !volSet->empty() ) {
10296       //int nodeSetSize = nodeSet->size();
10297
10298       // loop on given volumes
10299       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10300         SMDS_VolumeTool vol (*vIt);
10301         // loop on volume faces: find free faces
10302         // --------------------------------------
10303         list<const SMDS_MeshElement* > freeFaceList;
10304         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10305           if ( !vol.IsFreeFace( iFace ))
10306             continue;
10307           // check if there is already a face with same nodes in a face set
10308           const SMDS_MeshElement* aFreeFace = 0;
10309           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10310           int nbNodes = vol.NbFaceNodes( iFace );
10311           set <const SMDS_MeshNode*> faceNodeSet;
10312           vol.GetFaceNodes( iFace, faceNodeSet );
10313           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10314           if ( isNewFace ) {
10315             // no such a face is given but it still can exist, check it
10316             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10317             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10318           }
10319           if ( !aFreeFace ) {
10320             // create a temporary face
10321             if ( nbNodes == 3 ) {
10322               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10323               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10324             }
10325             else if ( nbNodes == 4 ) {
10326               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10327               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10328             }
10329             else {
10330               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10331               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10332               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10333             }
10334             if ( aFreeFace )
10335               tempFaceList.push_back( aFreeFace );
10336           }
10337
10338           if ( aFreeFace )
10339             freeFaceList.push_back( aFreeFace );
10340
10341         } // loop on faces of a volume
10342
10343         // choose one of several free faces of a volume
10344         // --------------------------------------------
10345         if ( freeFaceList.size() > 1 ) {
10346           // choose a face having max nb of nodes shared by other elems of a side
10347           int maxNbNodes = -1;
10348           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10349           while ( fIt != freeFaceList.end() ) { // loop on free faces
10350             int nbSharedNodes = 0;
10351             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10352             while ( nodeIt->more() ) { // loop on free face nodes
10353               const SMDS_MeshNode* n =
10354                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10355               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10356               while ( invElemIt->more() ) {
10357                 const SMDS_MeshElement* e = invElemIt->next();
10358                 nbSharedNodes += faceSet->count( e );
10359                 nbSharedNodes += elemSet->count( e );
10360               }
10361             }
10362             if ( nbSharedNodes > maxNbNodes ) {
10363               maxNbNodes = nbSharedNodes;
10364               freeFaceList.erase( freeFaceList.begin(), fIt++ );
10365             }
10366             else if ( nbSharedNodes == maxNbNodes ) {
10367               fIt++;
10368             }
10369             else {
10370               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10371             }
10372           }
10373           if ( freeFaceList.size() > 1 )
10374           {
10375             // could not choose one face, use another way
10376             // choose a face most close to the bary center of the opposite side
10377             gp_XYZ aBC( 0., 0., 0. );
10378             set <const SMDS_MeshNode*> addedNodes;
10379             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10380             eIt = elemSet2->begin();
10381             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10382               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10383               while ( nodeIt->more() ) { // loop on free face nodes
10384                 const SMDS_MeshNode* n =
10385                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10386                 if ( addedNodes.insert( n ).second )
10387                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10388               }
10389             }
10390             aBC /= addedNodes.size();
10391             double minDist = DBL_MAX;
10392             fIt = freeFaceList.begin();
10393             while ( fIt != freeFaceList.end() ) { // loop on free faces
10394               double dist = 0;
10395               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10396               while ( nodeIt->more() ) { // loop on free face nodes
10397                 const SMDS_MeshNode* n =
10398                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10399                 gp_XYZ p( n->X(),n->Y(),n->Z() );
10400                 dist += ( aBC - p ).SquareModulus();
10401               }
10402               if ( dist < minDist ) {
10403                 minDist = dist;
10404                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10405               }
10406               else
10407                 fIt = freeFaceList.erase( fIt++ );
10408             }
10409           }
10410         } // choose one of several free faces of a volume
10411
10412         if ( freeFaceList.size() == 1 ) {
10413           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10414           faceSet->insert( aFreeFace );
10415           // complete a node set with nodes of a found free face
10416           //           for ( iNode = 0; iNode < ; iNode++ )
10417           //             nodeSet->insert( fNodes[ iNode ] );
10418         }
10419
10420       } // loop on volumes of a side
10421
10422       //       // complete a set of faces if new nodes in a nodeSet appeared
10423       //       // ----------------------------------------------------------
10424       //       if ( nodeSetSize != nodeSet->size() ) {
10425       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10426       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10427       //           while ( fIt->more() ) { // loop on faces sharing a node
10428       //             const SMDS_MeshElement* f = fIt->next();
10429       //             if ( faceSet->find( f ) == faceSet->end() ) {
10430       //               // check if all nodes are in nodeSet and
10431       //               // complete setOfFaceNodeSet if they are
10432       //               set <const SMDS_MeshNode*> faceNodeSet;
10433       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10434       //               bool allInSet = true;
10435       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10436       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10437       //                 if ( nodeSet->find( n ) == nodeSet->end() )
10438       //                   allInSet = false;
10439       //                 else
10440       //                   faceNodeSet.insert( n );
10441       //               }
10442       //               if ( allInSet ) {
10443       //                 faceSet->insert( f );
10444       //                 setOfFaceNodeSet.insert( faceNodeSet );
10445       //               }
10446       //             }
10447       //           }
10448       //         }
10449       //       }
10450     } // Create temporary faces, if there are volumes given
10451   } // loop on sides
10452
10453   if ( faceSet1.size() != faceSet2.size() ) {
10454     // delete temporary faces: they are in reverseElements of actual nodes
10455 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10456 //    while ( tmpFaceIt->more() )
10457 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10458 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10459 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10460 //      aMesh->RemoveElement(*tmpFaceIt);
10461     MESSAGE("Diff nb of faces");
10462     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10463   }
10464
10465   // ============================================================
10466   // 2. Find nodes to merge:
10467   //              bind a node to remove to a node to put instead
10468   // ============================================================
10469
10470   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10471   if ( theFirstNode1 != theFirstNode2 )
10472     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10473   if ( theSecondNode1 != theSecondNode2 )
10474     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10475
10476   LinkID_Gen aLinkID_Gen( GetMeshDS() );
10477   set< long > linkIdSet; // links to process
10478   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10479
10480   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10481   list< NLink > linkList[2];
10482   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10483   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10484   // loop on links in linkList; find faces by links and append links
10485   // of the found faces to linkList
10486   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10487   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10488   {
10489     NLink link[] = { *linkIt[0], *linkIt[1] };
10490     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10491     if ( !linkIdSet.count( linkID ) )
10492       continue;
10493
10494     // by links, find faces in the face sets,
10495     // and find indices of link nodes in the found faces;
10496     // in a face set, there is only one or no face sharing a link
10497     // ---------------------------------------------------------------
10498
10499     const SMDS_MeshElement* face[] = { 0, 0 };
10500     vector<const SMDS_MeshNode*> fnodes[2];
10501     int iLinkNode[2][2];
10502     TIDSortedElemSet avoidSet;
10503     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10504       const SMDS_MeshNode* n1 = link[iSide].first;
10505       const SMDS_MeshNode* n2 = link[iSide].second;
10506       //cout << "Side " << iSide << " ";
10507       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10508       // find a face by two link nodes
10509       face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10510                                      &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10511       if ( face[ iSide ])
10512       {
10513         //cout << " F " << face[ iSide]->GetID() <<endl;
10514         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10515         // put face nodes to fnodes
10516         if ( face[ iSide ]->IsQuadratic() )
10517         {
10518           // use interlaced nodes iterator
10519           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10520           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10521           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10522           while ( nIter->more() )
10523             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10524         }
10525         else
10526         {
10527           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10528                                   face[ iSide ]->end_nodes() );
10529         }
10530         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10531       }
10532     }
10533
10534     // check similarity of elements of the sides
10535     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10536       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10537       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10538         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10539       }
10540       else {
10541         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10542       }
10543       break; // do not return because it's necessary to remove tmp faces
10544     }
10545
10546     // set nodes to merge
10547     // -------------------
10548
10549     if ( face[0] && face[1] )  {
10550       const int nbNodes = face[0]->NbNodes();
10551       if ( nbNodes != face[1]->NbNodes() ) {
10552         MESSAGE("Diff nb of face nodes");
10553         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10554         break; // do not return because it s necessary to remove tmp faces
10555       }
10556       bool reverse[] = { false, false }; // order of nodes in the link
10557       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10558         // analyse link orientation in faces
10559         int i1 = iLinkNode[ iSide ][ 0 ];
10560         int i2 = iLinkNode[ iSide ][ 1 ];
10561         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10562       }
10563       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10564       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10565       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10566       {
10567         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10568                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10569       }
10570
10571       // add other links of the faces to linkList
10572       // -----------------------------------------
10573
10574       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10575         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10576         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10577         if ( !iter_isnew.second ) { // already in a set: no need to process
10578           linkIdSet.erase( iter_isnew.first );
10579         }
10580         else // new in set == encountered for the first time: add
10581         {
10582           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10583           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10584           linkList[0].push_back ( NLink( n1, n2 ));
10585           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10586         }
10587       }
10588     } // 2 faces found
10589
10590     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10591       break;
10592
10593   } // loop on link lists
10594
10595   if ( aResult == SEW_OK &&
10596        ( //linkIt[0] != linkList[0].end() ||
10597          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10598     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10599              " " << (faceSetPtr[1]->empty()));
10600     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10601   }
10602
10603   // ====================================================================
10604   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10605   // ====================================================================
10606
10607   // delete temporary faces
10608 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10609 //  while ( tmpFaceIt->more() )
10610 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10611   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10612   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10613     aMesh->RemoveElement(*tmpFaceIt);
10614
10615   if ( aResult != SEW_OK)
10616     return aResult;
10617
10618   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10619   // loop on nodes replacement map
10620   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10621   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10622     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10623       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10624       nodeIDsToRemove.push_back( nToRemove->GetID() );
10625       // loop on elements sharing nToRemove
10626       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10627       while ( invElemIt->more() ) {
10628         const SMDS_MeshElement* e = invElemIt->next();
10629         // get a new suite of nodes: make replacement
10630         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10631         vector< const SMDS_MeshNode*> nodes( nbNodes );
10632         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10633         while ( nIt->more() ) {
10634           const SMDS_MeshNode* n =
10635             static_cast<const SMDS_MeshNode*>( nIt->next() );
10636           nnIt = nReplaceMap.find( n );
10637           if ( nnIt != nReplaceMap.end() ) {
10638             nbReplaced++;
10639             n = (*nnIt).second;
10640           }
10641           nodes[ i++ ] = n;
10642         }
10643         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10644         //         elemIDsToRemove.push_back( e->GetID() );
10645         //       else
10646         if ( nbReplaced )
10647           {
10648             SMDSAbs_ElementType etyp = e->GetType();
10649             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10650             if (newElem)
10651               {
10652                 myLastCreatedElems.Append(newElem);
10653                 AddToSameGroups(newElem, e, aMesh);
10654                 int aShapeId = e->getshapeId();
10655                 if ( aShapeId )
10656                   {
10657                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10658                   }
10659               }
10660             aMesh->RemoveElement(e);
10661           }
10662       }
10663     }
10664
10665   Remove( nodeIDsToRemove, true );
10666
10667   return aResult;
10668 }
10669
10670 //================================================================================
10671 /*!
10672  * \brief Find corresponding nodes in two sets of faces
10673  * \param theSide1 - first face set
10674  * \param theSide2 - second first face
10675  * \param theFirstNode1 - a boundary node of set 1
10676  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10677  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10678  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10679  * \param nReplaceMap - output map of corresponding nodes
10680  * \return bool  - is a success or not
10681  */
10682 //================================================================================
10683
10684 #ifdef _DEBUG_
10685 //#define DEBUG_MATCHING_NODES
10686 #endif
10687
10688 SMESH_MeshEditor::Sew_Error
10689 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10690                                     set<const SMDS_MeshElement*>& theSide2,
10691                                     const SMDS_MeshNode*          theFirstNode1,
10692                                     const SMDS_MeshNode*          theFirstNode2,
10693                                     const SMDS_MeshNode*          theSecondNode1,
10694                                     const SMDS_MeshNode*          theSecondNode2,
10695                                     TNodeNodeMap &                nReplaceMap)
10696 {
10697   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10698
10699   nReplaceMap.clear();
10700   if ( theFirstNode1 != theFirstNode2 )
10701     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10702   if ( theSecondNode1 != theSecondNode2 )
10703     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10704
10705   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10706   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10707
10708   list< NLink > linkList[2];
10709   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10710   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10711
10712   // loop on links in linkList; find faces by links and append links
10713   // of the found faces to linkList
10714   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10715   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10716     NLink link[] = { *linkIt[0], *linkIt[1] };
10717     if ( linkSet.find( link[0] ) == linkSet.end() )
10718       continue;
10719
10720     // by links, find faces in the face sets,
10721     // and find indices of link nodes in the found faces;
10722     // in a face set, there is only one or no face sharing a link
10723     // ---------------------------------------------------------------
10724
10725     const SMDS_MeshElement* face[] = { 0, 0 };
10726     list<const SMDS_MeshNode*> notLinkNodes[2];
10727     //bool reverse[] = { false, false }; // order of notLinkNodes
10728     int nbNodes[2];
10729     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10730     {
10731       const SMDS_MeshNode* n1 = link[iSide].first;
10732       const SMDS_MeshNode* n2 = link[iSide].second;
10733       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10734       set< const SMDS_MeshElement* > facesOfNode1;
10735       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10736       {
10737         // during a loop of the first node, we find all faces around n1,
10738         // during a loop of the second node, we find one face sharing both n1 and n2
10739         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10740         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10741         while ( fIt->more() ) { // loop on faces sharing a node
10742           const SMDS_MeshElement* f = fIt->next();
10743           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10744               ! facesOfNode1.insert( f ).second ) // f encounters twice
10745           {
10746             if ( face[ iSide ] ) {
10747               MESSAGE( "2 faces per link " );
10748               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10749             }
10750             face[ iSide ] = f;
10751             faceSet->erase( f );
10752
10753             // get not link nodes
10754             int nbN = f->NbNodes();
10755             if ( f->IsQuadratic() )
10756               nbN /= 2;
10757             nbNodes[ iSide ] = nbN;
10758             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10759             int i1 = f->GetNodeIndex( n1 );
10760             int i2 = f->GetNodeIndex( n2 );
10761             int iEnd = nbN, iBeg = -1, iDelta = 1;
10762             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10763             if ( reverse ) {
10764               std::swap( iEnd, iBeg ); iDelta = -1;
10765             }
10766             int i = i2;
10767             while ( true ) {
10768               i += iDelta;
10769               if ( i == iEnd ) i = iBeg + iDelta;
10770               if ( i == i1 ) break;
10771               nodes.push_back ( f->GetNode( i ) );
10772             }
10773           }
10774         }
10775       }
10776     }
10777     // check similarity of elements of the sides
10778     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10779       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10780       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10781         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10782       }
10783       else {
10784         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10785       }
10786     }
10787
10788     // set nodes to merge
10789     // -------------------
10790
10791     if ( face[0] && face[1] )  {
10792       if ( nbNodes[0] != nbNodes[1] ) {
10793         MESSAGE("Diff nb of face nodes");
10794         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10795       }
10796 #ifdef DEBUG_MATCHING_NODES
10797       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10798                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10799                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10800 #endif
10801       int nbN = nbNodes[0];
10802       {
10803         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10804         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10805         for ( int i = 0 ; i < nbN - 2; ++i ) {
10806 #ifdef DEBUG_MATCHING_NODES
10807           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10808 #endif
10809           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10810         }
10811       }
10812
10813       // add other links of the face 1 to linkList
10814       // -----------------------------------------
10815
10816       const SMDS_MeshElement* f0 = face[0];
10817       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10818       for ( int i = 0; i < nbN; i++ )
10819       {
10820         const SMDS_MeshNode* n2 = f0->GetNode( i );
10821         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10822           linkSet.insert( SMESH_TLink( n1, n2 ));
10823         if ( !iter_isnew.second ) { // already in a set: no need to process
10824           linkSet.erase( iter_isnew.first );
10825         }
10826         else // new in set == encountered for the first time: add
10827         {
10828 #ifdef DEBUG_MATCHING_NODES
10829           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10830                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10831 #endif
10832           linkList[0].push_back ( NLink( n1, n2 ));
10833           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10834         }
10835         n1 = n2;
10836       }
10837     } // 2 faces found
10838   } // loop on link lists
10839
10840   return SEW_OK;
10841 }
10842
10843 //================================================================================
10844 /*!
10845   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10846   \param theElems - the list of elements (edges or faces) to be replicated
10847   The nodes for duplication could be found from these elements
10848   \param theNodesNot - list of nodes to NOT replicate
10849   \param theAffectedElems - the list of elements (cells and edges) to which the
10850   replicated nodes should be associated to.
10851   \return TRUE if operation has been completed successfully, FALSE otherwise
10852 */
10853 //================================================================================
10854
10855 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10856                                     const TIDSortedElemSet& theNodesNot,
10857                                     const TIDSortedElemSet& theAffectedElems )
10858 {
10859   myLastCreatedElems.Clear();
10860   myLastCreatedNodes.Clear();
10861
10862   if ( theElems.size() == 0 )
10863     return false;
10864
10865   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10866   if ( !aMeshDS )
10867     return false;
10868
10869   bool res = false;
10870   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10871   // duplicate elements and nodes
10872   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10873   // replce nodes by duplications
10874   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10875   return res;
10876 }
10877
10878 //================================================================================
10879 /*!
10880   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10881   \param theMeshDS - mesh instance
10882   \param theElems - the elements replicated or modified (nodes should be changed)
10883   \param theNodesNot - nodes to NOT replicate
10884   \param theNodeNodeMap - relation of old node to new created node
10885   \param theIsDoubleElem - flag os to replicate element or modify
10886   \return TRUE if operation has been completed successfully, FALSE otherwise
10887 */
10888 //================================================================================
10889
10890 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10891                                     const TIDSortedElemSet& theElems,
10892                                     const TIDSortedElemSet& theNodesNot,
10893                                     std::map< const SMDS_MeshNode*,
10894                                     const SMDS_MeshNode* >& theNodeNodeMap,
10895                                     const bool theIsDoubleElem )
10896 {
10897   MESSAGE("doubleNodes");
10898   // iterate on through element and duplicate them (by nodes duplication)
10899   bool res = false;
10900   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10901   for ( ;  elemItr != theElems.end(); ++elemItr )
10902   {
10903     const SMDS_MeshElement* anElem = *elemItr;
10904     if (!anElem)
10905       continue;
10906
10907     bool isDuplicate = false;
10908     // duplicate nodes to duplicate element
10909     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10910     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10911     int ind = 0;
10912     while ( anIter->more() )
10913     {
10914
10915       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10916       SMDS_MeshNode* aNewNode = aCurrNode;
10917       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10918         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10919       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10920       {
10921         // duplicate node
10922         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10923         theNodeNodeMap[ aCurrNode ] = aNewNode;
10924         myLastCreatedNodes.Append( aNewNode );
10925       }
10926       isDuplicate |= (aCurrNode != aNewNode);
10927       newNodes[ ind++ ] = aNewNode;
10928     }
10929     if ( !isDuplicate )
10930       continue;
10931
10932     if ( theIsDoubleElem )
10933       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10934     else
10935       {
10936       MESSAGE("ChangeElementNodes");
10937       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10938       }
10939     res = true;
10940   }
10941   return res;
10942 }
10943
10944 //================================================================================
10945 /*!
10946   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10947   \param theNodes - identifiers of nodes to be doubled
10948   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10949          nodes. If list of element identifiers is empty then nodes are doubled but
10950          they not assigned to elements
10951   \return TRUE if operation has been completed successfully, FALSE otherwise
10952 */
10953 //================================================================================
10954
10955 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10956                                     const std::list< int >& theListOfModifiedElems )
10957 {
10958   MESSAGE("DoubleNodes");
10959   myLastCreatedElems.Clear();
10960   myLastCreatedNodes.Clear();
10961
10962   if ( theListOfNodes.size() == 0 )
10963     return false;
10964
10965   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10966   if ( !aMeshDS )
10967     return false;
10968
10969   // iterate through nodes and duplicate them
10970
10971   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10972
10973   std::list< int >::const_iterator aNodeIter;
10974   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10975   {
10976     int aCurr = *aNodeIter;
10977     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10978     if ( !aNode )
10979       continue;
10980
10981     // duplicate node
10982
10983     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10984     if ( aNewNode )
10985     {
10986       anOldNodeToNewNode[ aNode ] = aNewNode;
10987       myLastCreatedNodes.Append( aNewNode );
10988     }
10989   }
10990
10991   // Create map of new nodes for modified elements
10992
10993   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10994
10995   std::list< int >::const_iterator anElemIter;
10996   for ( anElemIter = theListOfModifiedElems.begin();
10997         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10998   {
10999     int aCurr = *anElemIter;
11000     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
11001     if ( !anElem )
11002       continue;
11003
11004     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
11005
11006     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11007     int ind = 0;
11008     while ( anIter->more() )
11009     {
11010       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
11011       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
11012       {
11013         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
11014         aNodeArr[ ind++ ] = aNewNode;
11015       }
11016       else
11017         aNodeArr[ ind++ ] = aCurrNode;
11018     }
11019     anElemToNodes[ anElem ] = aNodeArr;
11020   }
11021
11022   // Change nodes of elements
11023
11024   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
11025     anElemToNodesIter = anElemToNodes.begin();
11026   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
11027   {
11028     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
11029     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
11030     if ( anElem )
11031       {
11032       MESSAGE("ChangeElementNodes");
11033       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
11034       }
11035   }
11036
11037   return true;
11038 }
11039
11040 namespace {
11041
11042   //================================================================================
11043   /*!
11044   \brief Check if element located inside shape
11045   \return TRUE if IN or ON shape, FALSE otherwise
11046   */
11047   //================================================================================
11048
11049   template<class Classifier>
11050   bool isInside(const SMDS_MeshElement* theElem,
11051                 Classifier&             theClassifier,
11052                 const double            theTol)
11053   {
11054     gp_XYZ centerXYZ (0, 0, 0);
11055     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11056     while (aNodeItr->more())
11057       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
11058
11059     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11060     theClassifier.Perform(aPnt, theTol);
11061     TopAbs_State aState = theClassifier.State();
11062     return (aState == TopAbs_IN || aState == TopAbs_ON );
11063   }
11064
11065   //================================================================================
11066   /*!
11067    * \brief Classifier of the 3D point on the TopoDS_Face
11068    *        with interaface suitable for isInside()
11069    */
11070   //================================================================================
11071
11072   struct _FaceClassifier
11073   {
11074     Extrema_ExtPS       _extremum;
11075     BRepAdaptor_Surface _surface;
11076     TopAbs_State        _state;
11077
11078     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11079     {
11080       _extremum.Initialize( _surface,
11081                             _surface.FirstUParameter(), _surface.LastUParameter(),
11082                             _surface.FirstVParameter(), _surface.LastVParameter(),
11083                             _surface.Tolerance(), _surface.Tolerance() );
11084     }
11085     void Perform(const gp_Pnt& aPnt, double theTol)
11086     {
11087       _state = TopAbs_OUT;
11088       _extremum.Perform(aPnt);
11089       if ( _extremum.IsDone() )
11090         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11091 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
11092           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11093 #else
11094           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11095 #endif
11096     }
11097     TopAbs_State State() const
11098     {
11099       return _state;
11100     }
11101   };
11102 }
11103
11104 //================================================================================
11105 /*!
11106   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
11107   This method is the first step of DoubleNodeElemGroupsInRegion.
11108   \param theElems - list of groups of elements (edges or faces) to be replicated
11109   \param theNodesNot - list of groups of nodes not to replicated
11110   \param theShape - shape to detect affected elements (element which geometric center
11111          located on or inside shape).
11112          The replicated nodes should be associated to affected elements.
11113   \return groups of affected elements
11114   \sa DoubleNodeElemGroupsInRegion()
11115  */
11116 //================================================================================
11117
11118 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11119                                                    const TIDSortedElemSet& theNodesNot,
11120                                                    const TopoDS_Shape&     theShape,
11121                                                    TIDSortedElemSet&       theAffectedElems)
11122 {
11123   if ( theShape.IsNull() )
11124     return false;
11125
11126   const double aTol = Precision::Confusion();
11127   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11128   auto_ptr<_FaceClassifier>              aFaceClassifier;
11129   if ( theShape.ShapeType() == TopAbs_SOLID )
11130   {
11131     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11132     bsc3d->PerformInfinitePoint(aTol);
11133   }
11134   else if (theShape.ShapeType() == TopAbs_FACE )
11135   {
11136     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11137   }
11138
11139   // iterates on indicated elements and get elements by back references from their nodes
11140   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11141   for ( ;  elemItr != theElems.end(); ++elemItr )
11142   {
11143     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11144     if (!anElem)
11145       continue;
11146
11147     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11148     while ( nodeItr->more() )
11149     {
11150       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11151       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11152         continue;
11153       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11154       while ( backElemItr->more() )
11155       {
11156         const SMDS_MeshElement* curElem = backElemItr->next();
11157         if ( curElem && theElems.find(curElem) == theElems.end() &&
11158              ( bsc3d.get() ?
11159                isInside( curElem, *bsc3d, aTol ) :
11160                isInside( curElem, *aFaceClassifier, aTol )))
11161           theAffectedElems.insert( curElem );
11162       }
11163     }
11164   }
11165   return true;
11166 }
11167
11168 //================================================================================
11169 /*!
11170   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11171   \param theElems - group of of elements (edges or faces) to be replicated
11172   \param theNodesNot - group of nodes not to replicate
11173   \param theShape - shape to detect affected elements (element which geometric center
11174   located on or inside shape).
11175   The replicated nodes should be associated to affected elements.
11176   \return TRUE if operation has been completed successfully, FALSE otherwise
11177 */
11178 //================================================================================
11179
11180 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11181                                             const TIDSortedElemSet& theNodesNot,
11182                                             const TopoDS_Shape&     theShape )
11183 {
11184   if ( theShape.IsNull() )
11185     return false;
11186
11187   const double aTol = Precision::Confusion();
11188   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11189   auto_ptr<_FaceClassifier>              aFaceClassifier;
11190   if ( theShape.ShapeType() == TopAbs_SOLID )
11191   {
11192     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11193     bsc3d->PerformInfinitePoint(aTol);
11194   }
11195   else if (theShape.ShapeType() == TopAbs_FACE )
11196   {
11197     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11198   }
11199
11200   // iterates on indicated elements and get elements by back references from their nodes
11201   TIDSortedElemSet anAffected;
11202   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11203   for ( ;  elemItr != theElems.end(); ++elemItr )
11204   {
11205     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11206     if (!anElem)
11207       continue;
11208
11209     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11210     while ( nodeItr->more() )
11211     {
11212       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11213       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11214         continue;
11215       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11216       while ( backElemItr->more() )
11217       {
11218         const SMDS_MeshElement* curElem = backElemItr->next();
11219         if ( curElem && theElems.find(curElem) == theElems.end() &&
11220              ( bsc3d.get() ?
11221                isInside( curElem, *bsc3d, aTol ) :
11222                isInside( curElem, *aFaceClassifier, aTol )))
11223           anAffected.insert( curElem );
11224       }
11225     }
11226   }
11227   return DoubleNodes( theElems, theNodesNot, anAffected );
11228 }
11229
11230 /*!
11231  *  \brief compute an oriented angle between two planes defined by four points.
11232  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11233  *  @param p0 base of the rotation axe
11234  *  @param p1 extremity of the rotation axe
11235  *  @param g1 belongs to the first plane
11236  *  @param g2 belongs to the second plane
11237  */
11238 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11239 {
11240 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11241 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11242 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11243 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11244   gp_Vec vref(p0, p1);
11245   gp_Vec v1(p0, g1);
11246   gp_Vec v2(p0, g2);
11247   gp_Vec n1 = vref.Crossed(v1);
11248   gp_Vec n2 = vref.Crossed(v2);
11249   return n2.AngleWithRef(n1, vref);
11250 }
11251
11252 /*!
11253  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11254  * The list of groups must describe a partition of the mesh volumes.
11255  * The nodes of the internal faces at the boundaries of the groups are doubled.
11256  * In option, the internal faces are replaced by flat elements.
11257  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11258  * The flat elements are stored in groups of volumes.
11259  * @param theElems - list of groups of volumes, where a group of volume is a set of
11260  * SMDS_MeshElements sorted by Id.
11261  * @param createJointElems - if TRUE, create the elements
11262  * @return TRUE if operation has been completed successfully, FALSE otherwise
11263  */
11264 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11265                                                      bool createJointElems)
11266 {
11267   MESSAGE("----------------------------------------------");
11268   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11269   MESSAGE("----------------------------------------------");
11270
11271   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11272   meshDS->BuildDownWardConnectivity(true);
11273   CHRONO(50);
11274   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11275
11276   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11277   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11278   //     build the list of nodes shared by 2 or more domains, with their domain indexes
11279
11280   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11281   std::map<int,int>celldom; // cell vtkId --> domain
11282   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
11283   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
11284   faceDomains.clear();
11285   celldom.clear();
11286   cellDomains.clear();
11287   nodeDomains.clear();
11288   std::map<int,int> emptyMap;
11289   std::set<int> emptySet;
11290   emptyMap.clear();
11291
11292   for (int idom = 0; idom < theElems.size(); idom++)
11293     {
11294
11295       // --- build a map (face to duplicate --> volume to modify)
11296       //     with all the faces shared by 2 domains (group of elements)
11297       //     and corresponding volume of this domain, for each shared face.
11298       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11299
11300       //MESSAGE("Domain " << idom);
11301       const TIDSortedElemSet& domain = theElems[idom];
11302       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11303       for (; elemItr != domain.end(); ++elemItr)
11304         {
11305           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11306           if (!anElem)
11307             continue;
11308           int vtkId = anElem->getVtkId();
11309           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11310           int neighborsVtkIds[NBMAXNEIGHBORS];
11311           int downIds[NBMAXNEIGHBORS];
11312           unsigned char downTypes[NBMAXNEIGHBORS];
11313           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11314           for (int n = 0; n < nbNeighbors; n++)
11315             {
11316               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11317               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11318               if (! domain.count(elem)) // neighbor is in another domain : face is shared
11319                 {
11320                   DownIdType face(downIds[n], downTypes[n]);
11321                   if (!faceDomains.count(face))
11322                     faceDomains[face] = emptyMap; // create an empty entry for face
11323                   if (!faceDomains[face].count(idom))
11324                     {
11325                       faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11326                       celldom[vtkId] = idom;
11327                       //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11328                     }
11329                 }
11330             }
11331         }
11332     }
11333
11334   //MESSAGE("Number of shared faces " << faceDomains.size());
11335   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11336
11337   // --- explore the shared faces domain by domain,
11338   //     explore the nodes of the face and see if they belong to a cell in the domain,
11339   //     which has only a node or an edge on the border (not a shared face)
11340
11341   for (int idomain = 0; idomain < theElems.size(); idomain++)
11342     {
11343       //MESSAGE("Domain " << idomain);
11344       const TIDSortedElemSet& domain = theElems[idomain];
11345       itface = faceDomains.begin();
11346       for (; itface != faceDomains.end(); ++itface)
11347         {
11348           std::map<int, int> domvol = itface->second;
11349           if (!domvol.count(idomain))
11350             continue;
11351           DownIdType face = itface->first;
11352           //MESSAGE(" --- face " << face.cellId);
11353           std::set<int> oldNodes;
11354           oldNodes.clear();
11355           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11356           std::set<int>::iterator itn = oldNodes.begin();
11357           for (; itn != oldNodes.end(); ++itn)
11358             {
11359               int oldId = *itn;
11360               //MESSAGE("     node " << oldId);
11361               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11362               for (int i=0; i<l.ncells; i++)
11363                 {
11364                   int vtkId = l.cells[i];
11365                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11366                   if (!domain.count(anElem))
11367                     continue;
11368                   int vtkType = grid->GetCellType(vtkId);
11369                   int downId = grid->CellIdToDownId(vtkId);
11370                   if (downId < 0)
11371                     {
11372                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11373                       continue; // not OK at this stage of the algorithm:
11374                                 //no cells created after BuildDownWardConnectivity
11375                     }
11376                   DownIdType aCell(downId, vtkType);
11377                   if (!cellDomains.count(aCell))
11378                     cellDomains[aCell] = emptyMap; // create an empty entry for cell
11379                   cellDomains[aCell][idomain] = vtkId;
11380                   celldom[vtkId] = idomain;
11381                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11382                 }
11383             }
11384         }
11385     }
11386
11387   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11388   //     for each shared face, get the nodes
11389   //     for each node, for each domain of the face, create a clone of the node
11390
11391   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11392   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11393   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11394
11395   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11396   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11397   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11398
11399   for (int idomain = 0; idomain < theElems.size(); idomain++)
11400     {
11401       itface = faceDomains.begin();
11402       for (; itface != faceDomains.end(); ++itface)
11403         {
11404           std::map<int, int> domvol = itface->second;
11405           if (!domvol.count(idomain))
11406             continue;
11407           DownIdType face = itface->first;
11408           //MESSAGE(" --- face " << face.cellId);
11409           std::set<int> oldNodes;
11410           oldNodes.clear();
11411           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11412           std::set<int>::iterator itn = oldNodes.begin();
11413           for (; itn != oldNodes.end(); ++itn)
11414             {
11415               int oldId = *itn;
11416               //MESSAGE("-+-+-a node " << oldId);
11417               if (!nodeDomains.count(oldId))
11418                 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11419               if (nodeDomains[oldId].empty())
11420                 {
11421                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11422                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11423                 }
11424               std::map<int, int>::iterator itdom = domvol.begin();
11425               for (; itdom != domvol.end(); ++itdom)
11426                 {
11427                   int idom = itdom->first;
11428                   //MESSAGE("         domain " << idom);
11429                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11430                     {
11431                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11432                         {
11433                           vector<int> orderedDoms;
11434                           //MESSAGE("multiple node " << oldId);
11435                           if (mutipleNodes.count(oldId))
11436                             orderedDoms = mutipleNodes[oldId];
11437                           else
11438                             {
11439                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11440                               for (; it != nodeDomains[oldId].end(); ++it)
11441                                 orderedDoms.push_back(it->first);
11442                             }
11443                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11444                           //stringstream txt;
11445                           //for (int i=0; i<orderedDoms.size(); i++)
11446                           //  txt << orderedDoms[i] << " ";
11447                           //MESSAGE("orderedDoms " << txt.str());
11448                           mutipleNodes[oldId] = orderedDoms;
11449                         }
11450                       double *coords = grid->GetPoint(oldId);
11451                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11452                       int newId = newNode->getVtkId();
11453                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11454                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11455                     }
11456                 }
11457             }
11458         }
11459     }
11460
11461   for (int idomain = 0; idomain < theElems.size(); idomain++)
11462     {
11463       itface = faceDomains.begin();
11464       for (; itface != faceDomains.end(); ++itface)
11465         {
11466           std::map<int, int> domvol = itface->second;
11467           if (!domvol.count(idomain))
11468             continue;
11469           DownIdType face = itface->first;
11470           //MESSAGE(" --- face " << face.cellId);
11471           std::set<int> oldNodes;
11472           oldNodes.clear();
11473           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11474           int nbMultipleNodes = 0;
11475           std::set<int>::iterator itn = oldNodes.begin();
11476           for (; itn != oldNodes.end(); ++itn)
11477             {
11478               int oldId = *itn;
11479               if (mutipleNodes.count(oldId))
11480                 nbMultipleNodes++;
11481             }
11482           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11483             {
11484               //MESSAGE("multiple Nodes detected on a shared face");
11485               int downId = itface->first.cellId;
11486               unsigned char cellType = itface->first.cellType;
11487               // --- shared edge or shared face ?
11488               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11489                 {
11490                   int nodes[3];
11491                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11492                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11493                     if (mutipleNodes.count(nodes[i]))
11494                       if (!mutipleNodesToFace.count(nodes[i]))
11495                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11496                 }
11497               else // shared face (between two volumes)
11498                 {
11499                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11500                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11501                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11502                   for (int ie =0; ie < nbEdges; ie++)
11503                     {
11504                       int nodes[3];
11505                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11506                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11507                         {
11508                           vector<int> vn0 = mutipleNodes[nodes[0]];
11509                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11510                           vector<int> doms;
11511                           for (int i0 = 0; i0 < vn0.size(); i0++)
11512                             for (int i1 = 0; i1 < vn1.size(); i1++)
11513                               if (vn0[i0] == vn1[i1])
11514                                 doms.push_back(vn0[i0]);
11515                           if (doms.size() >2)
11516                             {
11517                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11518                               double *coords = grid->GetPoint(nodes[0]);
11519                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11520                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11521                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11522                               gp_Pnt gref;
11523                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11524                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11525                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11526                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11527                               for (int id=0; id < doms.size(); id++)
11528                                 {
11529                                   int idom = doms[id];
11530                                   for (int ivol=0; ivol<nbvol; ivol++)
11531                                     {
11532                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11533                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11534                                       if (theElems[idom].count(elem))
11535                                         {
11536                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11537                                           domvol[idom] = svol;
11538                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11539                                           double values[3];
11540                                           vtkIdType npts = 0;
11541                                           vtkIdType* pts = 0;
11542                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11543                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11544                                           if (id ==0)
11545                                             {
11546                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11547                                               angleDom[idom] = 0;
11548                                             }
11549                                           else
11550                                             {
11551                                               gp_Pnt g(values[0], values[1], values[2]);
11552                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11553                                               //MESSAGE("  angle=" << angleDom[idom]);
11554                                             }
11555                                           break;
11556                                         }
11557                                     }
11558                                 }
11559                               map<double, int> sortedDom; // sort domains by angle
11560                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11561                                 sortedDom[ia->second] = ia->first;
11562                               vector<int> vnodes;
11563                               vector<int> vdom;
11564                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11565                                 {
11566                                   vdom.push_back(ib->second);
11567                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11568                                 }
11569                               for (int ino = 0; ino < nbNodes; ino++)
11570                                 vnodes.push_back(nodes[ino]);
11571                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11572                             }
11573                         }
11574                     }
11575                 }
11576             }
11577         }
11578     }
11579
11580   // --- iterate on shared faces (volumes to modify, face to extrude)
11581   //     get node id's of the face (id SMDS = id VTK)
11582   //     create flat element with old and new nodes if requested
11583
11584   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11585   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11586
11587   std::map<int, std::map<long,int> > nodeQuadDomains;
11588   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11589
11590   if (createJointElems)
11591     {
11592       int idg;
11593       string joints2DName = "joints2D";
11594       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11595       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11596       string joints3DName = "joints3D";
11597       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11598       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11599
11600       itface = faceDomains.begin();
11601       for (; itface != faceDomains.end(); ++itface)
11602         {
11603           DownIdType face = itface->first;
11604           std::set<int> oldNodes;
11605           std::set<int>::iterator itn;
11606           oldNodes.clear();
11607           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11608
11609           std::map<int, int> domvol = itface->second;
11610           std::map<int, int>::iterator itdom = domvol.begin();
11611           int dom1 = itdom->first;
11612           int vtkVolId = itdom->second;
11613           itdom++;
11614           int dom2 = itdom->first;
11615           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11616                                                              nodeQuadDomains);
11617           stringstream grpname;
11618           grpname << "j_";
11619           if (dom1 < dom2)
11620             grpname << dom1 << "_" << dom2;
11621           else
11622             grpname << dom2 << "_" << dom1;
11623           string namegrp = grpname.str();
11624           if (!mapOfJunctionGroups.count(namegrp))
11625             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11626           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11627           if (sgrp)
11628             sgrp->Add(vol->GetID());
11629           if (vol->GetType() == SMDSAbs_Volume)
11630             joints3DGrp->Add(vol->GetID());
11631           else if (vol->GetType() == SMDSAbs_Face)
11632             joints2DGrp->Add(vol->GetID());
11633         }
11634     }
11635
11636   // --- create volumes on multiple domain intersection if requested
11637   //     iterate on mutipleNodesToFace
11638   //     iterate on edgesMultiDomains
11639
11640   if (createJointElems)
11641     {
11642       // --- iterate on mutipleNodesToFace
11643
11644       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11645       for (; itn != mutipleNodesToFace.end(); ++itn)
11646         {
11647           int node = itn->first;
11648           vector<int> orderDom = itn->second;
11649           vector<vtkIdType> orderedNodes;
11650           for (int idom = 0; idom <orderDom.size(); idom++)
11651             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11652             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11653
11654             stringstream grpname;
11655             grpname << "m2j_";
11656             grpname << 0 << "_" << 0;
11657             int idg;
11658             string namegrp = grpname.str();
11659             if (!mapOfJunctionGroups.count(namegrp))
11660               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11661             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11662             if (sgrp)
11663               sgrp->Add(face->GetID());
11664         }
11665
11666       // --- iterate on edgesMultiDomains
11667
11668       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11669       for (; ite != edgesMultiDomains.end(); ++ite)
11670         {
11671           vector<int> nodes = ite->first;
11672           vector<int> orderDom = ite->second;
11673           vector<vtkIdType> orderedNodes;
11674           if (nodes.size() == 2)
11675             {
11676               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11677               for (int ino=0; ino < nodes.size(); ino++)
11678                 if (orderDom.size() == 3)
11679                   for (int idom = 0; idom <orderDom.size(); idom++)
11680                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11681                 else
11682                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11683                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11684               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11685
11686               int idg;
11687               string namegrp = "jointsMultiples";
11688               if (!mapOfJunctionGroups.count(namegrp))
11689                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11690               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11691               if (sgrp)
11692                 sgrp->Add(vol->GetID());
11693             }
11694           else
11695             {
11696               INFOS("Quadratic multiple joints not implemented");
11697               // TODO quadratic nodes
11698             }
11699         }
11700     }
11701
11702   // --- list the explicit faces and edges of the mesh that need to be modified,
11703   //     i.e. faces and edges built with one or more duplicated nodes.
11704   //     associate these faces or edges to their corresponding domain.
11705   //     only the first domain found is kept when a face or edge is shared
11706
11707   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11708   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11709   faceOrEdgeDom.clear();
11710   feDom.clear();
11711
11712   for (int idomain = 0; idomain < theElems.size(); idomain++)
11713     {
11714       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11715       for (; itnod != nodeDomains.end(); ++itnod)
11716         {
11717           int oldId = itnod->first;
11718           //MESSAGE("     node " << oldId);
11719           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11720           for (int i = 0; i < l.ncells; i++)
11721             {
11722               int vtkId = l.cells[i];
11723               int vtkType = grid->GetCellType(vtkId);
11724               int downId = grid->CellIdToDownId(vtkId);
11725               if (downId < 0)
11726                 continue; // new cells: not to be modified
11727               DownIdType aCell(downId, vtkType);
11728               int volParents[1000];
11729               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11730               for (int j = 0; j < nbvol; j++)
11731                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11732                   if (!feDom.count(vtkId))
11733                     {
11734                       feDom[vtkId] = idomain;
11735                       faceOrEdgeDom[aCell] = emptyMap;
11736                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11737                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11738                       //        << " type " << vtkType << " downId " << downId);
11739                     }
11740             }
11741         }
11742     }
11743
11744   // --- iterate on shared faces (volumes to modify, face to extrude)
11745   //     get node id's of the face
11746   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11747
11748   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11749   for (int m=0; m<3; m++)
11750     {
11751       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11752       itface = (*amap).begin();
11753       for (; itface != (*amap).end(); ++itface)
11754         {
11755           DownIdType face = itface->first;
11756           std::set<int> oldNodes;
11757           std::set<int>::iterator itn;
11758           oldNodes.clear();
11759           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11760           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11761           std::map<int, int> localClonedNodeIds;
11762
11763           std::map<int, int> domvol = itface->second;
11764           std::map<int, int>::iterator itdom = domvol.begin();
11765           for (; itdom != domvol.end(); ++itdom)
11766             {
11767               int idom = itdom->first;
11768               int vtkVolId = itdom->second;
11769               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11770               localClonedNodeIds.clear();
11771               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11772                 {
11773                   int oldId = *itn;
11774                   if (nodeDomains[oldId].count(idom))
11775                     {
11776                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11777                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11778                     }
11779                 }
11780               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11781             }
11782         }
11783     }
11784
11785   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11786   grid->BuildLinks();
11787
11788   CHRONOSTOP(50);
11789   counters::stats();
11790   return true;
11791 }
11792
11793 /*!
11794  * \brief Double nodes on some external faces and create flat elements.
11795  * Flat elements are mainly used by some types of mechanic calculations.
11796  *
11797  * Each group of the list must be constituted of faces.
11798  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11799  * @param theElems - list of groups of faces, where a group of faces is a set of
11800  * SMDS_MeshElements sorted by Id.
11801  * @return TRUE if operation has been completed successfully, FALSE otherwise
11802  */
11803 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11804 {
11805   MESSAGE("-------------------------------------------------");
11806   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11807   MESSAGE("-------------------------------------------------");
11808
11809   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11810
11811   // --- For each group of faces
11812   //     duplicate the nodes, create a flat element based on the face
11813   //     replace the nodes of the faces by their clones
11814
11815   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11816   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11817   clonedNodes.clear();
11818   intermediateNodes.clear();
11819   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11820   mapOfJunctionGroups.clear();
11821
11822   for (int idom = 0; idom < theElems.size(); idom++)
11823     {
11824       const TIDSortedElemSet& domain = theElems[idom];
11825       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11826       for (; elemItr != domain.end(); ++elemItr)
11827         {
11828           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11829           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11830           if (!aFace)
11831             continue;
11832           // MESSAGE("aFace=" << aFace->GetID());
11833           bool isQuad = aFace->IsQuadratic();
11834           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11835
11836           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11837
11838           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11839           while (nodeIt->more())
11840             {
11841               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11842               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11843               if (isMedium)
11844                 ln2.push_back(node);
11845               else
11846                 ln0.push_back(node);
11847
11848               const SMDS_MeshNode* clone = 0;
11849               if (!clonedNodes.count(node))
11850                 {
11851                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11852                   clonedNodes[node] = clone;
11853                 }
11854               else
11855                 clone = clonedNodes[node];
11856
11857               if (isMedium)
11858                 ln3.push_back(clone);
11859               else
11860                 ln1.push_back(clone);
11861
11862               const SMDS_MeshNode* inter = 0;
11863               if (isQuad && (!isMedium))
11864                 {
11865                   if (!intermediateNodes.count(node))
11866                     {
11867                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11868                       intermediateNodes[node] = inter;
11869                     }
11870                   else
11871                     inter = intermediateNodes[node];
11872                   ln4.push_back(inter);
11873                 }
11874             }
11875
11876           // --- extrude the face
11877
11878           vector<const SMDS_MeshNode*> ln;
11879           SMDS_MeshVolume* vol = 0;
11880           vtkIdType aType = aFace->GetVtkType();
11881           switch (aType)
11882           {
11883             case VTK_TRIANGLE:
11884               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11885               // MESSAGE("vol prism " << vol->GetID());
11886               ln.push_back(ln1[0]);
11887               ln.push_back(ln1[1]);
11888               ln.push_back(ln1[2]);
11889               break;
11890             case VTK_QUAD:
11891               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11892               // MESSAGE("vol hexa " << vol->GetID());
11893               ln.push_back(ln1[0]);
11894               ln.push_back(ln1[1]);
11895               ln.push_back(ln1[2]);
11896               ln.push_back(ln1[3]);
11897               break;
11898             case VTK_QUADRATIC_TRIANGLE:
11899               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11900                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11901               // MESSAGE("vol quad prism " << vol->GetID());
11902               ln.push_back(ln1[0]);
11903               ln.push_back(ln1[1]);
11904               ln.push_back(ln1[2]);
11905               ln.push_back(ln3[0]);
11906               ln.push_back(ln3[1]);
11907               ln.push_back(ln3[2]);
11908               break;
11909             case VTK_QUADRATIC_QUAD:
11910 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11911 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11912 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11913               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11914                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11915                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11916               // MESSAGE("vol quad hexa " << vol->GetID());
11917               ln.push_back(ln1[0]);
11918               ln.push_back(ln1[1]);
11919               ln.push_back(ln1[2]);
11920               ln.push_back(ln1[3]);
11921               ln.push_back(ln3[0]);
11922               ln.push_back(ln3[1]);
11923               ln.push_back(ln3[2]);
11924               ln.push_back(ln3[3]);
11925               break;
11926             case VTK_POLYGON:
11927               break;
11928             default:
11929               break;
11930           }
11931
11932           if (vol)
11933             {
11934               stringstream grpname;
11935               grpname << "jf_";
11936               grpname << idom;
11937               int idg;
11938               string namegrp = grpname.str();
11939               if (!mapOfJunctionGroups.count(namegrp))
11940                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11941               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11942               if (sgrp)
11943                 sgrp->Add(vol->GetID());
11944             }
11945
11946           // --- modify the face
11947
11948           aFace->ChangeNodes(&ln[0], ln.size());
11949         }
11950     }
11951   return true;
11952 }
11953
11954 /*!
11955  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11956  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11957  *  groups of faces to remove inside the object, (idem edges).
11958  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11959  */
11960 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11961                                       const TopoDS_Shape& theShape,
11962                                       SMESH_NodeSearcher* theNodeSearcher,
11963                                       const char* groupName,
11964                                       std::vector<double>&   nodesCoords,
11965                                       std::vector<std::vector<int> >& listOfListOfNodes)
11966 {
11967   MESSAGE("--------------------------------");
11968   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11969   MESSAGE("--------------------------------");
11970
11971   // --- zone of volumes to remove is given :
11972   //     1 either by a geom shape (one or more vertices) and a radius,
11973   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11974   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11975   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11976   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11977   //     defined by it's name.
11978
11979   SMESHDS_GroupBase* groupDS = 0;
11980   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11981   while ( groupIt->more() )
11982     {
11983       groupDS = 0;
11984       SMESH_Group * group = groupIt->next();
11985       if ( !group ) continue;
11986       groupDS = group->GetGroupDS();
11987       if ( !groupDS || groupDS->IsEmpty() ) continue;
11988       std::string grpName = group->GetName();
11989       //MESSAGE("grpName=" << grpName);
11990       if (grpName == groupName)
11991         break;
11992       else
11993         groupDS = 0;
11994     }
11995
11996   bool isNodeGroup = false;
11997   bool isNodeCoords = false;
11998   if (groupDS)
11999     {
12000       if (groupDS->GetType() != SMDSAbs_Node)
12001         return;
12002       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
12003     }
12004
12005   if (nodesCoords.size() > 0)
12006     isNodeCoords = true; // a list o nodes given by their coordinates
12007   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12008
12009   // --- define groups to build
12010
12011   int idg; // --- group of SMDS volumes
12012   string grpvName = groupName;
12013   grpvName += "_vol";
12014   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12015   if (!grp)
12016     {
12017       MESSAGE("group not created " << grpvName);
12018       return;
12019     }
12020   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12021
12022   int idgs; // --- group of SMDS faces on the skin
12023   string grpsName = groupName;
12024   grpsName += "_skin";
12025   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12026   if (!grps)
12027     {
12028       MESSAGE("group not created " << grpsName);
12029       return;
12030     }
12031   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12032
12033   int idgi; // --- group of SMDS faces internal (several shapes)
12034   string grpiName = groupName;
12035   grpiName += "_internalFaces";
12036   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12037   if (!grpi)
12038     {
12039       MESSAGE("group not created " << grpiName);
12040       return;
12041     }
12042   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12043
12044   int idgei; // --- group of SMDS faces internal (several shapes)
12045   string grpeiName = groupName;
12046   grpeiName += "_internalEdges";
12047   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12048   if (!grpei)
12049     {
12050       MESSAGE("group not created " << grpeiName);
12051       return;
12052     }
12053   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12054
12055   // --- build downward connectivity
12056
12057   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12058   meshDS->BuildDownWardConnectivity(true);
12059   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12060
12061   // --- set of volumes detected inside
12062
12063   std::set<int> setOfInsideVol;
12064   std::set<int> setOfVolToCheck;
12065
12066   std::vector<gp_Pnt> gpnts;
12067   gpnts.clear();
12068
12069   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12070     {
12071       MESSAGE("group of nodes provided");
12072       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12073       while ( elemIt->more() )
12074         {
12075           const SMDS_MeshElement* elem = elemIt->next();
12076           if (!elem)
12077             continue;
12078           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12079           if (!node)
12080             continue;
12081           SMDS_MeshElement* vol = 0;
12082           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12083           while (volItr->more())
12084             {
12085               vol = (SMDS_MeshElement*)volItr->next();
12086               setOfInsideVol.insert(vol->getVtkId());
12087               sgrp->Add(vol->GetID());
12088             }
12089         }
12090     }
12091   else if (isNodeCoords)
12092     {
12093       MESSAGE("list of nodes coordinates provided");
12094       int i = 0;
12095       int k = 0;
12096       while (i < nodesCoords.size()-2)
12097         {
12098           double x = nodesCoords[i++];
12099           double y = nodesCoords[i++];
12100           double z = nodesCoords[i++];
12101           gp_Pnt p = gp_Pnt(x, y ,z);
12102           gpnts.push_back(p);
12103           MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
12104         }
12105     }
12106   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12107     {
12108       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12109       TopTools_IndexedMapOfShape vertexMap;
12110       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12111       gp_Pnt p = gp_Pnt(0,0,0);
12112       if (vertexMap.Extent() < 1)
12113         return;
12114
12115       for ( int i = 1; i <= vertexMap.Extent(); ++i )
12116         {
12117           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12118           p = BRep_Tool::Pnt(vertex);
12119           gpnts.push_back(p);
12120           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12121         }
12122     }
12123
12124   if (gpnts.size() > 0)
12125     {
12126       int nodeId = 0;
12127       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12128       if (startNode)
12129         nodeId = startNode->GetID();
12130       MESSAGE("nodeId " << nodeId);
12131
12132       double radius2 = radius*radius;
12133       MESSAGE("radius2 " << radius2);
12134
12135       // --- volumes on start node
12136
12137       setOfVolToCheck.clear();
12138       SMDS_MeshElement* startVol = 0;
12139       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12140       while (volItr->more())
12141         {
12142           startVol = (SMDS_MeshElement*)volItr->next();
12143           setOfVolToCheck.insert(startVol->getVtkId());
12144         }
12145       if (setOfVolToCheck.empty())
12146         {
12147           MESSAGE("No volumes found");
12148           return;
12149         }
12150
12151       // --- starting with central volumes then their neighbors, check if they are inside
12152       //     or outside the domain, until no more new neighbor volume is inside.
12153       //     Fill the group of inside volumes
12154
12155       std::map<int, double> mapOfNodeDistance2;
12156       mapOfNodeDistance2.clear();
12157       std::set<int> setOfOutsideVol;
12158       while (!setOfVolToCheck.empty())
12159         {
12160           std::set<int>::iterator it = setOfVolToCheck.begin();
12161           int vtkId = *it;
12162           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12163           bool volInside = false;
12164           vtkIdType npts = 0;
12165           vtkIdType* pts = 0;
12166           grid->GetCellPoints(vtkId, npts, pts);
12167           for (int i=0; i<npts; i++)
12168             {
12169               double distance2 = 0;
12170               if (mapOfNodeDistance2.count(pts[i]))
12171                 {
12172                   distance2 = mapOfNodeDistance2[pts[i]];
12173                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
12174                 }
12175               else
12176                 {
12177                   double *coords = grid->GetPoint(pts[i]);
12178                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12179                   distance2 = 1.E40;
12180                   for (int j=0; j<gpnts.size(); j++)
12181                     {
12182                       double d2 = aPoint.SquareDistance(gpnts[j]);
12183                       if (d2 < distance2)
12184                         {
12185                           distance2 = d2;
12186                           if (distance2 < radius2)
12187                             break;
12188                         }
12189                     }
12190                   mapOfNodeDistance2[pts[i]] = distance2;
12191                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
12192                 }
12193               if (distance2 < radius2)
12194                 {
12195                   volInside = true; // one or more nodes inside the domain
12196                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12197                   break;
12198                 }
12199             }
12200           if (volInside)
12201             {
12202               setOfInsideVol.insert(vtkId);
12203               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12204               int neighborsVtkIds[NBMAXNEIGHBORS];
12205               int downIds[NBMAXNEIGHBORS];
12206               unsigned char downTypes[NBMAXNEIGHBORS];
12207               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12208               for (int n = 0; n < nbNeighbors; n++)
12209                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12210                   setOfVolToCheck.insert(neighborsVtkIds[n]);
12211             }
12212           else
12213             {
12214               setOfOutsideVol.insert(vtkId);
12215               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12216             }
12217           setOfVolToCheck.erase(vtkId);
12218         }
12219     }
12220
12221   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12222   //     If yes, add the volume to the inside set
12223
12224   bool addedInside = true;
12225   std::set<int> setOfVolToReCheck;
12226   while (addedInside)
12227     {
12228       MESSAGE(" --------------------------- re check");
12229       addedInside = false;
12230       std::set<int>::iterator itv = setOfInsideVol.begin();
12231       for (; itv != setOfInsideVol.end(); ++itv)
12232         {
12233           int vtkId = *itv;
12234           int neighborsVtkIds[NBMAXNEIGHBORS];
12235           int downIds[NBMAXNEIGHBORS];
12236           unsigned char downTypes[NBMAXNEIGHBORS];
12237           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12238           for (int n = 0; n < nbNeighbors; n++)
12239             if (!setOfInsideVol.count(neighborsVtkIds[n]))
12240               setOfVolToReCheck.insert(neighborsVtkIds[n]);
12241         }
12242       setOfVolToCheck = setOfVolToReCheck;
12243       setOfVolToReCheck.clear();
12244       while  (!setOfVolToCheck.empty())
12245         {
12246           std::set<int>::iterator it = setOfVolToCheck.begin();
12247           int vtkId = *it;
12248           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12249             {
12250               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12251               int countInside = 0;
12252               int neighborsVtkIds[NBMAXNEIGHBORS];
12253               int downIds[NBMAXNEIGHBORS];
12254               unsigned char downTypes[NBMAXNEIGHBORS];
12255               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12256               for (int n = 0; n < nbNeighbors; n++)
12257                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12258                   countInside++;
12259               MESSAGE("countInside " << countInside);
12260               if (countInside > 1)
12261                 {
12262                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12263                   setOfInsideVol.insert(vtkId);
12264                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12265                   addedInside = true;
12266                 }
12267               else
12268                 setOfVolToReCheck.insert(vtkId);
12269             }
12270           setOfVolToCheck.erase(vtkId);
12271         }
12272     }
12273
12274   // --- map of Downward faces at the boundary, inside the global volume
12275   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12276   //     fill group of SMDS faces inside the volume (when several volume shapes)
12277   //     fill group of SMDS faces on the skin of the global volume (if skin)
12278
12279   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12280   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12281   std::set<int>::iterator it = setOfInsideVol.begin();
12282   for (; it != setOfInsideVol.end(); ++it)
12283     {
12284       int vtkId = *it;
12285       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12286       int neighborsVtkIds[NBMAXNEIGHBORS];
12287       int downIds[NBMAXNEIGHBORS];
12288       unsigned char downTypes[NBMAXNEIGHBORS];
12289       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12290       for (int n = 0; n < nbNeighbors; n++)
12291         {
12292           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12293           if (neighborDim == 3)
12294             {
12295               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12296                 {
12297                   DownIdType face(downIds[n], downTypes[n]);
12298                   boundaryFaces[face] = vtkId;
12299                 }
12300               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12301               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12302               if (vtkFaceId >= 0)
12303                 {
12304                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12305                   // find also the smds edges on this face
12306                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12307                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12308                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12309                   for (int i = 0; i < nbEdges; i++)
12310                     {
12311                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12312                       if (vtkEdgeId >= 0)
12313                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12314                     }
12315                 }
12316             }
12317           else if (neighborDim == 2) // skin of the volume
12318             {
12319               DownIdType face(downIds[n], downTypes[n]);
12320               skinFaces[face] = vtkId;
12321               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12322               if (vtkFaceId >= 0)
12323                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12324             }
12325         }
12326     }
12327
12328   // --- identify the edges constituting the wire of each subshape on the skin
12329   //     define polylines with the nodes of edges, equivalent to wires
12330   //     project polylines on subshapes, and partition, to get geom faces
12331
12332   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12333   std::set<int> emptySet;
12334   emptySet.clear();
12335   std::set<int> shapeIds;
12336
12337   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12338   while (itelem->more())
12339     {
12340       const SMDS_MeshElement *elem = itelem->next();
12341       int shapeId = elem->getshapeId();
12342       int vtkId = elem->getVtkId();
12343       if (!shapeIdToVtkIdSet.count(shapeId))
12344         {
12345           shapeIdToVtkIdSet[shapeId] = emptySet;
12346           shapeIds.insert(shapeId);
12347         }
12348       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12349     }
12350
12351   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12352   std::set<DownIdType, DownIdCompare> emptyEdges;
12353   emptyEdges.clear();
12354
12355   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12356   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12357     {
12358       int shapeId = itShape->first;
12359       MESSAGE(" --- Shape ID --- "<< shapeId);
12360       shapeIdToEdges[shapeId] = emptyEdges;
12361
12362       std::vector<int> nodesEdges;
12363
12364       std::set<int>::iterator its = itShape->second.begin();
12365       for (; its != itShape->second.end(); ++its)
12366         {
12367           int vtkId = *its;
12368           MESSAGE("     " << vtkId);
12369           int neighborsVtkIds[NBMAXNEIGHBORS];
12370           int downIds[NBMAXNEIGHBORS];
12371           unsigned char downTypes[NBMAXNEIGHBORS];
12372           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12373           for (int n = 0; n < nbNeighbors; n++)
12374             {
12375               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12376                 continue;
12377               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12378               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12379               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12380                 {
12381                   DownIdType edge(downIds[n], downTypes[n]);
12382                   if (!shapeIdToEdges[shapeId].count(edge))
12383                     {
12384                       shapeIdToEdges[shapeId].insert(edge);
12385                       int vtkNodeId[3];
12386                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12387                       nodesEdges.push_back(vtkNodeId[0]);
12388                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12389                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12390                     }
12391                 }
12392             }
12393         }
12394
12395       std::list<int> order;
12396       order.clear();
12397       if (nodesEdges.size() > 0)
12398         {
12399           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12400           nodesEdges[0] = -1;
12401           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12402           nodesEdges[1] = -1; // do not reuse this edge
12403           bool found = true;
12404           while (found)
12405             {
12406               int nodeTofind = order.back(); // try first to push back
12407               int i = 0;
12408               for (i = 0; i<nodesEdges.size(); i++)
12409                 if (nodesEdges[i] == nodeTofind)
12410                   break;
12411               if (i == nodesEdges.size())
12412                 found = false; // no follower found on back
12413               else
12414                 {
12415                   if (i%2) // odd ==> use the previous one
12416                     if (nodesEdges[i-1] < 0)
12417                       found = false;
12418                     else
12419                       {
12420                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12421                         nodesEdges[i-1] = -1;
12422                       }
12423                   else // even ==> use the next one
12424                     if (nodesEdges[i+1] < 0)
12425                       found = false;
12426                     else
12427                       {
12428                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12429                         nodesEdges[i+1] = -1;
12430                       }
12431                 }
12432               if (found)
12433                 continue;
12434               // try to push front
12435               found = true;
12436               nodeTofind = order.front(); // try to push front
12437               for (i = 0; i<nodesEdges.size(); i++)
12438                 if (nodesEdges[i] == nodeTofind)
12439                   break;
12440               if (i == nodesEdges.size())
12441                 {
12442                   found = false; // no predecessor found on front
12443                   continue;
12444                 }
12445               if (i%2) // odd ==> use the previous one
12446                 if (nodesEdges[i-1] < 0)
12447                   found = false;
12448                 else
12449                   {
12450                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12451                     nodesEdges[i-1] = -1;
12452                   }
12453               else // even ==> use the next one
12454                 if (nodesEdges[i+1] < 0)
12455                   found = false;
12456                 else
12457                   {
12458                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12459                     nodesEdges[i+1] = -1;
12460                   }
12461             }
12462         }
12463
12464
12465       std::vector<int> nodes;
12466       nodes.push_back(shapeId);
12467       std::list<int>::iterator itl = order.begin();
12468       for (; itl != order.end(); itl++)
12469         {
12470           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12471           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12472         }
12473       listOfListOfNodes.push_back(nodes);
12474     }
12475
12476   //     partition geom faces with blocFissure
12477   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12478   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12479
12480   return;
12481 }
12482
12483
12484 //================================================================================
12485 /*!
12486  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12487  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12488  * \return TRUE if operation has been completed successfully, FALSE otherwise
12489  */
12490 //================================================================================
12491
12492 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12493 {
12494   // iterates on volume elements and detect all free faces on them
12495   SMESHDS_Mesh* aMesh = GetMeshDS();
12496   if (!aMesh)
12497     return false;
12498   //bool res = false;
12499   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12500   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12501   while(vIt->more())
12502   {
12503     const SMDS_MeshVolume* volume = vIt->next();
12504     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12505     vTool.SetExternalNormal();
12506     //const bool isPoly = volume->IsPoly();
12507     const int iQuad = volume->IsQuadratic();
12508     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12509     {
12510       if (!vTool.IsFreeFace(iface))
12511         continue;
12512       nbFree++;
12513       vector<const SMDS_MeshNode *> nodes;
12514       int nbFaceNodes = vTool.NbFaceNodes(iface);
12515       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12516       int inode = 0;
12517       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12518         nodes.push_back(faceNodes[inode]);
12519       if (iQuad) { // add medium nodes
12520         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12521           nodes.push_back(faceNodes[inode]);
12522         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12523           nodes.push_back(faceNodes[8]);
12524       }
12525       // add new face based on volume nodes
12526       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12527         nbExisted++;
12528         continue; // face already exsist
12529       }
12530       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12531       nbCreated++;
12532     }
12533   }
12534   return ( nbFree==(nbExisted+nbCreated) );
12535 }
12536
12537 namespace
12538 {
12539   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12540   {
12541     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12542       return n;
12543     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12544   }
12545 }
12546 //================================================================================
12547 /*!
12548  * \brief Creates missing boundary elements
12549  *  \param elements - elements whose boundary is to be checked
12550  *  \param dimension - defines type of boundary elements to create
12551  *  \param group - a group to store created boundary elements in
12552  *  \param targetMesh - a mesh to store created boundary elements in
12553  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12554  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12555  *                                boundary elements will be copied into the targetMesh
12556  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12557  *                                boundary elements will be added into the new group
12558  *  \param aroundElements - if true, elements will be created on boundary of given
12559  *                          elements else, on boundary of the whole mesh.
12560  * \return nb of added boundary elements
12561  */
12562 //================================================================================
12563
12564 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12565                                        Bnd_Dimension           dimension,
12566                                        SMESH_Group*            group/*=0*/,
12567                                        SMESH_Mesh*             targetMesh/*=0*/,
12568                                        bool                    toCopyElements/*=false*/,
12569                                        bool                    toCopyExistingBoundary/*=false*/,
12570                                        bool                    toAddExistingBondary/*= false*/,
12571                                        bool                    aroundElements/*= false*/)
12572 {
12573   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12574   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12575   // hope that all elements are of the same type, do not check them all
12576   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12577     throw SALOME_Exception(LOCALIZED("wrong element type"));
12578
12579   if ( !targetMesh )
12580     toCopyElements = toCopyExistingBoundary = false;
12581
12582   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12583   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12584   int nbAddedBnd = 0;
12585
12586   // editor adding present bnd elements and optionally holding elements to add to the group
12587   SMESH_MeshEditor* presentEditor;
12588   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12589   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12590
12591   SMESH_MesherHelper helper( *myMesh );
12592   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12593   SMDS_VolumeTool vTool;
12594   TIDSortedElemSet avoidSet;
12595   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12596   int inode;
12597
12598   typedef vector<const SMDS_MeshNode*> TConnectivity;
12599
12600   SMDS_ElemIteratorPtr eIt;
12601   if (elements.empty())
12602     eIt = aMesh->elementsIterator(elemType);
12603   else
12604     eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12605
12606   while (eIt->more())
12607   {
12608     const SMDS_MeshElement* elem = eIt->next();
12609     const int iQuad = elem->IsQuadratic();
12610
12611     // ------------------------------------------------------------------------------------
12612     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12613     // ------------------------------------------------------------------------------------
12614     vector<const SMDS_MeshElement*> presentBndElems;
12615     vector<TConnectivity>           missingBndElems;
12616     TConnectivity nodes;
12617     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12618     {
12619       vTool.SetExternalNormal();
12620       const SMDS_MeshElement* otherVol = 0;
12621       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12622       {
12623         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12624              ( !aroundElements || elements.count( otherVol )))
12625           continue;
12626         const int nbFaceNodes = vTool.NbFaceNodes(iface);
12627         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12628         if ( missType == SMDSAbs_Edge ) // boundary edges
12629         {
12630           nodes.resize( 2+iQuad );
12631           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12632           {
12633             for ( int j = 0; j < nodes.size(); ++j )
12634               nodes[j] =nn[i+j];
12635             if ( const SMDS_MeshElement* edge =
12636                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12637               presentBndElems.push_back( edge );
12638             else
12639               missingBndElems.push_back( nodes );
12640           }
12641         }
12642         else // boundary face
12643         {
12644           nodes.clear();
12645           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12646             nodes.push_back( nn[inode] );
12647           if (iQuad) // add medium nodes
12648             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12649               nodes.push_back( nn[inode] );
12650           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12651           if ( iCenter > 0 )
12652             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12653
12654           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12655                                                                SMDSAbs_Face, /*noMedium=*/false ))
12656             presentBndElems.push_back( f );
12657           else
12658             missingBndElems.push_back( nodes );
12659
12660           if ( targetMesh != myMesh )
12661           {
12662             // add 1D elements on face boundary to be added to a new mesh
12663             const SMDS_MeshElement* edge;
12664             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12665             {
12666               if ( iQuad )
12667                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12668               else
12669                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12670               if ( edge && avoidSet.insert( edge ).second )
12671                 presentBndElems.push_back( edge );
12672             }
12673           }
12674         }
12675       }
12676     }
12677     else                     // elem is a face ------------------------------------------
12678     {
12679       avoidSet.clear(), avoidSet.insert( elem );
12680       int nbNodes = elem->NbCornerNodes();
12681       nodes.resize( 2 /*+ iQuad*/);
12682       for ( int i = 0; i < nbNodes; i++ )
12683       {
12684         nodes[0] = elem->GetNode(i);
12685         nodes[1] = elem->GetNode((i+1)%nbNodes);
12686         if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12687           continue; // not free link
12688
12689         //if ( iQuad )
12690         //nodes[2] = elem->GetNode( i + nbNodes );
12691         if ( const SMDS_MeshElement* edge =
12692              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12693           presentBndElems.push_back( edge );
12694         else
12695           missingBndElems.push_back( nodes );
12696       }
12697     }
12698
12699     // ---------------------------------
12700     // 2. Add missing boundary elements
12701     // ---------------------------------
12702     if ( targetMesh != myMesh )
12703       // instead of making a map of nodes in this mesh and targetMesh,
12704       // we create nodes with same IDs.
12705       for ( int i = 0; i < missingBndElems.size(); ++i )
12706       {
12707         TConnectivity& srcNodes = missingBndElems[i];
12708         TConnectivity  nodes( srcNodes.size() );
12709         for ( inode = 0; inode < nodes.size(); ++inode )
12710           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12711         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12712                                                                    missType,
12713                                                                    /*noMedium=*/false))
12714           continue;
12715         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12716         ++nbAddedBnd;
12717       }
12718     else
12719       for ( int i = 0; i < missingBndElems.size(); ++i )
12720       {
12721         TConnectivity& nodes = missingBndElems[i];
12722         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12723                                                                    missType,
12724                                                                    /*noMedium=*/false))
12725           continue;
12726         SMDS_MeshElement* elem =
12727           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12728         ++nbAddedBnd;
12729
12730         // try to set a new element to a shape
12731         if ( myMesh->HasShapeToMesh() )
12732         {
12733           bool ok = true;
12734           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12735           const int nbN = nodes.size() / (iQuad+1 );
12736           for ( inode = 0; inode < nbN && ok; ++inode )
12737           {
12738             pair<int, TopAbs_ShapeEnum> i_stype =
12739               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12740             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12741               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12742           }
12743           if ( ok && mediumShapes.size() > 1 )
12744           {
12745             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12746             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12747             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12748             {
12749               if (( ok = ( stype_i->first != stype_i_0.first )))
12750                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12751                                         aMesh->IndexToShape( stype_i_0.second ));
12752             }
12753           }
12754           if ( ok && mediumShapes.begin()->first == missShapeType )
12755             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12756         }
12757       }
12758
12759     // ----------------------------------
12760     // 3. Copy present boundary elements
12761     // ----------------------------------
12762     if ( toCopyExistingBoundary )
12763       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12764       {
12765         const SMDS_MeshElement* e = presentBndElems[i];
12766         TConnectivity nodes( e->NbNodes() );
12767         for ( inode = 0; inode < nodes.size(); ++inode )
12768           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12769         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12770       }
12771     else // store present elements to add them to a group
12772       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12773       {
12774         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12775       }
12776
12777   } // loop on given elements
12778
12779   // ---------------------------------------------
12780   // 4. Fill group with boundary elements
12781   // ---------------------------------------------
12782   if ( group )
12783   {
12784     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12785       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12786         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12787   }
12788   tgtEditor.myLastCreatedElems.Clear();
12789   tgtEditor2.myLastCreatedElems.Clear();
12790
12791   // -----------------------
12792   // 5. Copy given elements
12793   // -----------------------
12794   if ( toCopyElements && targetMesh != myMesh )
12795   {
12796     if (elements.empty())
12797       eIt = aMesh->elementsIterator(elemType);
12798     else
12799       eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12800     while (eIt->more())
12801     {
12802       const SMDS_MeshElement* elem = eIt->next();
12803       TConnectivity nodes( elem->NbNodes() );
12804       for ( inode = 0; inode < nodes.size(); ++inode )
12805         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12806       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12807
12808       tgtEditor.myLastCreatedElems.Clear();
12809     }
12810   }
12811   return nbAddedBnd;
12812 }