Salome HOME
52457: Addition of hypotheses is 8 time longer than meshing.
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2014  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, or (at your option) any later version.
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_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Face.hxx>
76 #include <TopoDS_Solid.hxx>
77 #include <gp.hxx>
78 #include <gp_Ax1.hxx>
79 #include <gp_Dir.hxx>
80 #include <gp_Lin.hxx>
81 #include <gp_Pln.hxx>
82 #include <gp_Trsf.hxx>
83 #include <gp_Vec.hxx>
84 #include <gp_XY.hxx>
85 #include <gp_XYZ.hxx>
86
87 #include <cmath>
88
89 #include <map>
90 #include <set>
91 #include <numeric>
92 #include <limits>
93 #include <algorithm>
94 #include <sstream>
95
96 #include <boost/tuple/tuple.hpp>
97
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
100
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
102
103 using namespace std;
104 using namespace SMESH::Controls;
105
106 namespace
107 {
108   template < class ELEM_SET >
109   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
110   {
111     typedef SMDS_SetIterator
112       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
114   }
115 }
116
117 //=======================================================================
118 //function : SMESH_MeshEditor
119 //purpose  :
120 //=======================================================================
121
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123   :myMesh( theMesh ) // theMesh may be NULL
124 {
125 }
126
127 //================================================================================
128 /*!
129  * \brief Clears myLastCreatedNodes and myLastCreatedElems
130  */
131 //================================================================================
132
133 void SMESH_MeshEditor::CrearLastCreated()
134 {
135   myLastCreatedNodes.Clear();
136   myLastCreatedElems.Clear();
137 }
138
139
140 //=======================================================================
141 /*!
142  * \brief Add element
143  */
144 //=======================================================================
145
146 SMDS_MeshElement*
147 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
148                              const SMDSAbs_ElementType            type,
149                              const bool                           isPoly,
150                              const int                            ID,
151                              const double                         ballDiameter)
152 {
153   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
154   SMDS_MeshElement* e = 0;
155   int nbnode = node.size();
156   SMESHDS_Mesh* mesh = GetMeshDS();
157   switch ( type ) {
158   case SMDSAbs_Face:
159     if ( !isPoly ) {
160       if      (nbnode == 3) {
161         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
162         else           e = mesh->AddFace      (node[0], node[1], node[2] );
163       }
164       else if (nbnode == 4) {
165         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
166         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
167       }
168       else if (nbnode == 6) {
169         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
170                                                node[4], node[5], ID);
171         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
172                                                node[4], node[5] );
173       }
174       else if (nbnode == 7) {
175         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
176                                                node[4], node[5], node[6], ID);
177         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
178                                                node[4], node[5], node[6] );
179       }
180       else if (nbnode == 8) {
181         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
182                                                node[4], node[5], node[6], node[7], ID);
183         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
184                                                node[4], node[5], node[6], node[7] );
185       }
186       else if (nbnode == 9) {
187         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
188                                                node[4], node[5], node[6], node[7], node[8], ID);
189         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
190                                                node[4], node[5], node[6], node[7], node[8] );
191       }
192     } else {
193       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
194       else           e = mesh->AddPolygonalFace      (node    );
195     }
196     break;
197
198   case SMDSAbs_Volume:
199     if ( !isPoly ) {
200       if      (nbnode == 4) {
201         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
202         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
203       }
204       else if (nbnode == 5) {
205         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
206                                                  node[4], ID);
207         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
208                                                  node[4] );
209       }
210       else if (nbnode == 6) {
211         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212                                                  node[4], node[5], ID);
213         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
214                                                  node[4], node[5] );
215       }
216       else if (nbnode == 8) {
217         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
218                                                  node[4], node[5], node[6], node[7], ID);
219         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
220                                                  node[4], node[5], node[6], node[7] );
221       }
222       else if (nbnode == 10) {
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], 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] );
229       }
230       else if (nbnode == 12) {
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], ID);
234         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
235                                                  node[4], node[5], node[6], node[7],
236                                                  node[8], node[9], node[10], node[11] );
237       }
238       else if (nbnode == 13) {
239         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240                                                  node[4], node[5], node[6], node[7],
241                                                  node[8], node[9], node[10],node[11],
242                                                  node[12],ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
244                                                  node[4], node[5], node[6], node[7],
245                                                  node[8], node[9], node[10],node[11],
246                                                  node[12] );
247       }
248       else if (nbnode == 15) {
249         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250                                                  node[4], node[5], node[6], node[7],
251                                                  node[8], node[9], node[10],node[11],
252                                                  node[12],node[13],node[14],ID);
253         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
254                                                  node[4], node[5], node[6], node[7],
255                                                  node[8], node[9], node[10],node[11],
256                                                  node[12],node[13],node[14] );
257       }
258       else if (nbnode == 20) {
259         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260                                                  node[4], node[5], node[6], node[7],
261                                                  node[8], node[9], node[10],node[11],
262                                                  node[12],node[13],node[14],node[15],
263                                                  node[16],node[17],node[18],node[19],ID);
264         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], node[10],node[11],
267                                                  node[12],node[13],node[14],node[15],
268                                                  node[16],node[17],node[18],node[19] );
269       }
270       else if (nbnode == 27) {
271         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272                                                  node[4], node[5], node[6], node[7],
273                                                  node[8], node[9], node[10],node[11],
274                                                  node[12],node[13],node[14],node[15],
275                                                  node[16],node[17],node[18],node[19],
276                                                  node[20],node[21],node[22],node[23],
277                                                  node[24],node[25],node[26], ID);
278         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
279                                                  node[4], node[5], node[6], node[7],
280                                                  node[8], node[9], node[10],node[11],
281                                                  node[12],node[13],node[14],node[15],
282                                                  node[16],node[17],node[18],node[19],
283                                                  node[20],node[21],node[22],node[23],
284                                                  node[24],node[25],node[26] );
285       }
286     }
287     break;
288
289   case SMDSAbs_Edge:
290     if ( nbnode == 2 ) {
291       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
292       else           e = mesh->AddEdge      (node[0], node[1] );
293     }
294     else if ( nbnode == 3 ) {
295       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
296       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
297     }
298     break;
299
300   case SMDSAbs_0DElement:
301     if ( nbnode == 1 ) {
302       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
303       else           e = mesh->Add0DElement      (node[0] );
304     }
305     break;
306
307   case SMDSAbs_Node:
308     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
309     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
310     break;
311
312   case SMDSAbs_Ball:
313     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
314     else           e = mesh->AddBall      (node[0], ballDiameter);
315     break;
316
317   default:;
318   }
319   if ( e ) myLastCreatedElems.Append( e );
320   return e;
321 }
322
323 //=======================================================================
324 /*!
325  * \brief Add element
326  */
327 //=======================================================================
328
329 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
330                                                const SMDSAbs_ElementType type,
331                                                const bool                isPoly,
332                                                const int                 ID)
333 {
334   vector<const SMDS_MeshNode*> nodes;
335   nodes.reserve( nodeIDs.size() );
336   vector<int>::const_iterator id = nodeIDs.begin();
337   while ( id != nodeIDs.end() ) {
338     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
339       nodes.push_back( node );
340     else
341       return 0;
342   }
343   return AddElement( nodes, type, isPoly, ID );
344 }
345
346 //=======================================================================
347 //function : Remove
348 //purpose  : Remove a node or an element.
349 //           Modify a compute state of sub-meshes which become empty
350 //=======================================================================
351
352 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
353                               const bool         isNodes )
354 {
355   myLastCreatedElems.Clear();
356   myLastCreatedNodes.Clear();
357
358   SMESHDS_Mesh* aMesh = GetMeshDS();
359   set< SMESH_subMesh *> smmap;
360
361   int removed = 0;
362   list<int>::const_iterator it = theIDs.begin();
363   for ( ; it != theIDs.end(); it++ ) {
364     const SMDS_MeshElement * elem;
365     if ( isNodes )
366       elem = aMesh->FindNode( *it );
367     else
368       elem = aMesh->FindElement( *it );
369     if ( !elem )
370       continue;
371
372     // Notify VERTEX sub-meshes about modification
373     if ( isNodes ) {
374       const SMDS_MeshNode* node = cast2Node( elem );
375       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
376         if ( int aShapeID = node->getshapeId() )
377           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378             smmap.insert( sm );
379     }
380     // Find sub-meshes to notify about modification
381     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
382     //     while ( nodeIt->more() ) {
383     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
384     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
385     //       if ( aPosition.get() ) {
386     //         if ( int aShapeID = aPosition->GetShapeId() ) {
387     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
388     //             smmap.insert( sm );
389     //         }
390     //       }
391     //     }
392
393     // Do remove
394     if ( isNodes )
395       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
396     else
397       aMesh->RemoveElement( elem );
398     removed++;
399   }
400
401   // Notify sub-meshes about modification
402   if ( !smmap.empty() ) {
403     set< SMESH_subMesh *>::iterator smIt;
404     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
405       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
406   }
407
408   //   // Check if the whole mesh becomes empty
409   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
410   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
411
412   return removed;
413 }
414
415 //================================================================================
416 /*!
417  * \brief Create 0D elements on all nodes of the given object except those
418  *        nodes on which a 0D element already exists.
419  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
420  *                    the all mesh is treated
421  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
422  */
423 //================================================================================
424
425 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
426                                                    TIDSortedElemSet&       all0DElems )
427 {
428   SMDS_ElemIteratorPtr elemIt;
429   vector< const SMDS_MeshElement* > allNodes;
430   if ( elements.empty() )
431   {
432     allNodes.reserve( GetMeshDS()->NbNodes() );
433     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
434     while ( elemIt->more() )
435       allNodes.push_back( elemIt->next() );
436
437     elemIt = elemSetIterator( allNodes );
438   }
439   else
440   {
441     elemIt = elemSetIterator( elements );
442   }
443
444   while ( elemIt->more() )
445   {
446     const SMDS_MeshElement* e = elemIt->next();
447     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
448     while ( nodeIt->more() )
449     {
450       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
451       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
452       if ( it0D->more() )
453         all0DElems.insert( it0D->next() );
454       else {
455         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
456         all0DElems.insert( myLastCreatedElems.Last() );
457       }
458     }
459   }
460 }
461
462 //=======================================================================
463 //function : FindShape
464 //purpose  : Return an index of the shape theElem is on
465 //           or zero if a shape not found
466 //=======================================================================
467
468 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
469 {
470   myLastCreatedElems.Clear();
471   myLastCreatedNodes.Clear();
472
473   SMESHDS_Mesh * aMesh = GetMeshDS();
474   if ( aMesh->ShapeToMesh().IsNull() )
475     return 0;
476
477   int aShapeID = theElem->getshapeId();
478   if ( aShapeID < 1 )
479     return 0;
480
481   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
482     if ( sm->Contains( theElem ))
483       return aShapeID;
484
485   if ( theElem->GetType() == SMDSAbs_Node ) {
486     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
487   }
488   else {
489     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
490   }
491
492   TopoDS_Shape aShape; // the shape a node of theElem is on
493   if ( theElem->GetType() != SMDSAbs_Node )
494   {
495     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
496     while ( nodeIt->more() ) {
497       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
498       if ((aShapeID = node->getshapeId()) > 0) {
499         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
500           if ( sm->Contains( theElem ))
501             return aShapeID;
502           if ( aShape.IsNull() )
503             aShape = aMesh->IndexToShape( aShapeID );
504         }
505       }
506     }
507   }
508
509   // None of nodes is on a proper shape,
510   // find the shape among ancestors of aShape on which a node is
511   if ( !aShape.IsNull() ) {
512     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
513     for ( ; ancIt.More(); ancIt.Next() ) {
514       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
515       if ( sm && sm->Contains( theElem ))
516         return aMesh->ShapeToIndex( ancIt.Value() );
517     }
518   }
519   else
520   {
521     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
522     while ( const SMESHDS_SubMesh* sm = smIt->next() )
523       if ( sm->Contains( theElem ))
524         return sm->GetID();
525   }
526
527   return 0;
528 }
529
530 //=======================================================================
531 //function : IsMedium
532 //purpose  :
533 //=======================================================================
534
535 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
536                                 const SMDSAbs_ElementType typeToCheck)
537 {
538   bool isMedium = false;
539   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
540   while (it->more() && !isMedium ) {
541     const SMDS_MeshElement* elem = it->next();
542     isMedium = elem->IsMediumNode(node);
543   }
544   return isMedium;
545 }
546
547 //=======================================================================
548 //function : shiftNodesQuadTria
549 //purpose  : Shift nodes in the array corresponded to quadratic triangle
550 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
551 //=======================================================================
552
553 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
554 {
555   const SMDS_MeshNode* nd1 = aNodes[0];
556   aNodes[0] = aNodes[1];
557   aNodes[1] = aNodes[2];
558   aNodes[2] = nd1;
559   const SMDS_MeshNode* nd2 = aNodes[3];
560   aNodes[3] = aNodes[4];
561   aNodes[4] = aNodes[5];
562   aNodes[5] = nd2;
563 }
564
565 //=======================================================================
566 //function : nbEdgeConnectivity
567 //purpose  : return number of the edges connected with the theNode.
568 //           if theEdges has connections with the other type of the
569 //           elements, return -1
570 //=======================================================================
571
572 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
573 {
574   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
575   // int nb=0;
576   // while(elemIt->more()) {
577   //   elemIt->next();
578   //   nb++;
579   // }
580   // return nb;
581   return theNode->NbInverseElements();
582 }
583
584 //=======================================================================
585 //function : getNodesFromTwoTria
586 //purpose  : 
587 //=======================================================================
588
589 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
590                                 const SMDS_MeshElement * theTria2,
591                                 vector< const SMDS_MeshNode*>& N1,
592                                 vector< const SMDS_MeshNode*>& N2)
593 {
594   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
595   if ( N1.size() < 6 ) return false;
596   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
597   if ( N2.size() < 6 ) return false;
598
599   int sames[3] = {-1,-1,-1};
600   int nbsames = 0;
601   int i, j;
602   for(i=0; i<3; i++) {
603     for(j=0; j<3; j++) {
604       if(N1[i]==N2[j]) {
605         sames[i] = j;
606         nbsames++;
607         break;
608       }
609     }
610   }
611   if(nbsames!=2) return false;
612   if(sames[0]>-1) {
613     shiftNodesQuadTria(N1);
614     if(sames[1]>-1) {
615       shiftNodesQuadTria(N1);
616     }
617   }
618   i = sames[0] + sames[1] + sames[2];
619   for(; i<2; i++) {
620     shiftNodesQuadTria(N2);
621   }
622   // now we receive following N1 and N2 (using numeration as in the image below)
623   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
624   // i.e. first nodes from both arrays form a new diagonal
625   return true;
626 }
627
628 //=======================================================================
629 //function : InverseDiag
630 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
631 //           but having other common link.
632 //           Return False if args are improper
633 //=======================================================================
634
635 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
636                                     const SMDS_MeshElement * theTria2 )
637 {
638   MESSAGE("InverseDiag");
639   myLastCreatedElems.Clear();
640   myLastCreatedNodes.Clear();
641
642   if (!theTria1 || !theTria2)
643     return false;
644
645   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
646   if (!F1) return false;
647   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
648   if (!F2) return false;
649   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
650       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
651
652     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
653     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
654     //    |/ |                                         | \|
655     //  B +--+ 2                                     B +--+ 2
656
657     // put nodes in array and find out indices of the same ones
658     const SMDS_MeshNode* aNodes [6];
659     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
660     int i = 0;
661     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
662     while ( it->more() ) {
663       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
664
665       if ( i > 2 ) // theTria2
666         // find same node of theTria1
667         for ( int j = 0; j < 3; j++ )
668           if ( aNodes[ i ] == aNodes[ j ]) {
669             sameInd[ j ] = i;
670             sameInd[ i ] = j;
671             break;
672           }
673       // next
674       i++;
675       if ( i == 3 ) {
676         if ( it->more() )
677           return false; // theTria1 is not a triangle
678         it = theTria2->nodesIterator();
679       }
680       if ( i == 6 && it->more() )
681         return false; // theTria2 is not a triangle
682     }
683
684     // find indices of 1,2 and of A,B in theTria1
685     int iA = -1, iB = 0, i1 = 0, i2 = 0;
686     for ( i = 0; i < 6; i++ ) {
687       if ( sameInd [ i ] == -1 ) {
688         if ( i < 3 ) i1 = i;
689         else         i2 = i;
690       }
691       else if (i < 3) {
692         if ( iA >= 0) iB = i;
693         else          iA = i;
694       }
695     }
696     // nodes 1 and 2 should not be the same
697     if ( aNodes[ i1 ] == aNodes[ i2 ] )
698       return false;
699
700     // theTria1: A->2
701     aNodes[ iA ] = aNodes[ i2 ];
702     // theTria2: B->1
703     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
704
705     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
706     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
707
708     return true;
709
710   } // end if(F1 && F2)
711
712   // check case of quadratic faces
713   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
714       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
715     return false;
716   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
717       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
718     return false;
719
720   //       5
721   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
722   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
723   //    |   / |
724   //  7 +  +  + 6
725   //    | /9  |
726   //    |/    |
727   //  4 +--+--+ 3
728   //       8
729
730   vector< const SMDS_MeshNode* > N1;
731   vector< const SMDS_MeshNode* > N2;
732   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
733     return false;
734   // now we receive following N1 and N2 (using numeration as above image)
735   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
736   // i.e. first nodes from both arrays determ new diagonal
737
738   vector< const SMDS_MeshNode*> N1new( N1.size() );
739   vector< const SMDS_MeshNode*> N2new( N2.size() );
740   N1new.back() = N1.back(); // central node of biquadratic
741   N2new.back() = N2.back();
742   N1new[0] = N1[0];  N2new[0] = N1[0];
743   N1new[1] = N2[0];  N2new[1] = N1[1];
744   N1new[2] = N2[1];  N2new[2] = N2[0];
745   N1new[3] = N1[4];  N2new[3] = N1[3];
746   N1new[4] = N2[3];  N2new[4] = N2[5];
747   N1new[5] = N1[5];  N2new[5] = N1[4];
748   // change nodes in faces
749   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
750   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
751
752   // move the central node of biquadratic triangle
753   SMESH_MesherHelper helper( *GetMesh() );
754   for ( int is2nd = 0; is2nd < 2; ++is2nd )
755   {
756     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
757     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
758     if ( nodes.size() < 7 )
759       continue;
760     helper.SetSubShape( tria->getshapeId() );
761     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
762     gp_Pnt xyz;
763     if ( F.IsNull() )
764     {
765       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
766               SMESH_TNodeXYZ( nodes[4] ) +
767               SMESH_TNodeXYZ( nodes[5] )) / 3.;
768     }
769     else
770     {
771       bool checkUV;
772       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
773                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
774                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
775       TopLoc_Location loc;
776       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
777       xyz = S->Value( uv.X(), uv.Y() );
778       xyz.Transform( loc );
779       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
780            nodes[6]->getshapeId() > 0 )
781         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
782     }
783     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
784   }
785   return true;
786 }
787
788 //=======================================================================
789 //function : findTriangles
790 //purpose  : find triangles sharing theNode1-theNode2 link
791 //=======================================================================
792
793 static bool findTriangles(const SMDS_MeshNode *    theNode1,
794                           const SMDS_MeshNode *    theNode2,
795                           const SMDS_MeshElement*& theTria1,
796                           const SMDS_MeshElement*& theTria2)
797 {
798   if ( !theNode1 || !theNode2 ) return false;
799
800   theTria1 = theTria2 = 0;
801
802   set< const SMDS_MeshElement* > emap;
803   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
804   while (it->more()) {
805     const SMDS_MeshElement* elem = it->next();
806     if ( elem->NbCornerNodes() == 3 )
807       emap.insert( elem );
808   }
809   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
810   while (it->more()) {
811     const SMDS_MeshElement* elem = it->next();
812     if ( emap.count( elem )) {
813       if ( !theTria1 )
814       {
815         theTria1 = elem;
816       }
817       else  
818       {
819         theTria2 = elem;
820         // theTria1 must be element with minimum ID
821         if ( theTria2->GetID() < theTria1->GetID() )
822           std::swap( theTria2, theTria1 );
823         return true;
824       }
825     }
826   }
827   return false;
828 }
829
830 //=======================================================================
831 //function : InverseDiag
832 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
833 //           with ones built on the same 4 nodes but having other common link.
834 //           Return false if proper faces not found
835 //=======================================================================
836
837 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
838                                     const SMDS_MeshNode * theNode2)
839 {
840   myLastCreatedElems.Clear();
841   myLastCreatedNodes.Clear();
842
843   MESSAGE( "::InverseDiag()" );
844
845   const SMDS_MeshElement *tr1, *tr2;
846   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
847     return false;
848
849   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
850   if (!F1) return false;
851   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
852   if (!F2) return false;
853   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
854       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
855
856     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
857     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
858     //    |/ |                                    | \|
859     //  B +--+ 2                                B +--+ 2
860
861     // put nodes in array
862     // and find indices of 1,2 and of A in tr1 and of B in tr2
863     int i, iA1 = 0, i1 = 0;
864     const SMDS_MeshNode* aNodes1 [3];
865     SMDS_ElemIteratorPtr it;
866     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
867       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
868       if ( aNodes1[ i ] == theNode1 )
869         iA1 = i; // node A in tr1
870       else if ( aNodes1[ i ] != theNode2 )
871         i1 = i;  // node 1
872     }
873     int iB2 = 0, i2 = 0;
874     const SMDS_MeshNode* aNodes2 [3];
875     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
876       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
877       if ( aNodes2[ i ] == theNode2 )
878         iB2 = i; // node B in tr2
879       else if ( aNodes2[ i ] != theNode1 )
880         i2 = i;  // node 2
881     }
882
883     // nodes 1 and 2 should not be the same
884     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
885       return false;
886
887     // tr1: A->2
888     aNodes1[ iA1 ] = aNodes2[ i2 ];
889     // tr2: B->1
890     aNodes2[ iB2 ] = aNodes1[ i1 ];
891
892     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
893     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
894
895     return true;
896   }
897
898   // check case of quadratic faces
899   return InverseDiag(tr1,tr2);
900 }
901
902 //=======================================================================
903 //function : getQuadrangleNodes
904 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
905 //           fusion of triangles tr1 and tr2 having shared link on
906 //           theNode1 and theNode2
907 //=======================================================================
908
909 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
910                         const SMDS_MeshNode *    theNode1,
911                         const SMDS_MeshNode *    theNode2,
912                         const SMDS_MeshElement * tr1,
913                         const SMDS_MeshElement * tr2 )
914 {
915   if( tr1->NbNodes() != tr2->NbNodes() )
916     return false;
917   // find the 4-th node to insert into tr1
918   const SMDS_MeshNode* n4 = 0;
919   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
920   int i=0;
921   while ( !n4 && i<3 ) {
922     const SMDS_MeshNode * n = cast2Node( it->next() );
923     i++;
924     bool isDiag = ( n == theNode1 || n == theNode2 );
925     if ( !isDiag )
926       n4 = n;
927   }
928   // Make an array of nodes to be in a quadrangle
929   int iNode = 0, iFirstDiag = -1;
930   it = tr1->nodesIterator();
931   i=0;
932   while ( i<3 ) {
933     const SMDS_MeshNode * n = cast2Node( it->next() );
934     i++;
935     bool isDiag = ( n == theNode1 || n == theNode2 );
936     if ( isDiag ) {
937       if ( iFirstDiag < 0 )
938         iFirstDiag = iNode;
939       else if ( iNode - iFirstDiag == 1 )
940         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
941     }
942     else if ( n == n4 ) {
943       return false; // tr1 and tr2 should not have all the same nodes
944     }
945     theQuadNodes[ iNode++ ] = n;
946   }
947   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
948     theQuadNodes[ iNode ] = n4;
949
950   return true;
951 }
952
953 //=======================================================================
954 //function : DeleteDiag
955 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
956 //           with a quadrangle built on the same 4 nodes.
957 //           Return false if proper faces not found
958 //=======================================================================
959
960 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
961                                    const SMDS_MeshNode * theNode2)
962 {
963   myLastCreatedElems.Clear();
964   myLastCreatedNodes.Clear();
965
966   MESSAGE( "::DeleteDiag()" );
967
968   const SMDS_MeshElement *tr1, *tr2;
969   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
970     return false;
971
972   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
973   if (!F1) return false;
974   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
975   if (!F2) return false;
976   SMESHDS_Mesh * aMesh = GetMeshDS();
977
978   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
979       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
980
981     const SMDS_MeshNode* aNodes [ 4 ];
982     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
983       return false;
984
985     const SMDS_MeshElement* newElem = 0;
986     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
987     myLastCreatedElems.Append(newElem);
988     AddToSameGroups( newElem, tr1, aMesh );
989     int aShapeId = tr1->getshapeId();
990     if ( aShapeId )
991       {
992         aMesh->SetMeshElementOnShape( newElem, aShapeId );
993       }
994     aMesh->RemoveElement( tr1 );
995     aMesh->RemoveElement( tr2 );
996
997     return true;
998   }
999
1000   // check case of quadratic faces
1001   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1002     return false;
1003   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1004     return false;
1005
1006   //       5
1007   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1008   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1009   //    |   / |
1010   //  7 +  +  + 6
1011   //    | /9  |
1012   //    |/    |
1013   //  4 +--+--+ 3
1014   //       8
1015
1016   vector< const SMDS_MeshNode* > N1;
1017   vector< const SMDS_MeshNode* > N2;
1018   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1019     return false;
1020   // now we receive following N1 and N2 (using numeration as above image)
1021   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1022   // i.e. first nodes from both arrays determ new diagonal
1023
1024   const SMDS_MeshNode* aNodes[8];
1025   aNodes[0] = N1[0];
1026   aNodes[1] = N1[1];
1027   aNodes[2] = N2[0];
1028   aNodes[3] = N2[1];
1029   aNodes[4] = N1[3];
1030   aNodes[5] = N2[5];
1031   aNodes[6] = N2[3];
1032   aNodes[7] = N1[5];
1033
1034   const SMDS_MeshElement* newElem = 0;
1035   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1036                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1037   myLastCreatedElems.Append(newElem);
1038   AddToSameGroups( newElem, tr1, aMesh );
1039   int aShapeId = tr1->getshapeId();
1040   if ( aShapeId )
1041     {
1042       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1043     }
1044   aMesh->RemoveElement( tr1 );
1045   aMesh->RemoveElement( tr2 );
1046
1047   // remove middle node (9)
1048   GetMeshDS()->RemoveNode( N1[4] );
1049
1050   return true;
1051 }
1052
1053 //=======================================================================
1054 //function : Reorient
1055 //purpose  : Reverse theElement orientation
1056 //=======================================================================
1057
1058 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1059 {
1060   MESSAGE("Reorient");
1061   myLastCreatedElems.Clear();
1062   myLastCreatedNodes.Clear();
1063
1064   if (!theElem)
1065     return false;
1066   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1067   if ( !it || !it->more() )
1068     return false;
1069
1070   const SMDSAbs_ElementType type = theElem->GetType();
1071   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1072     return false;
1073
1074   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1075   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1076   {
1077     const SMDS_VtkVolume* aPolyedre =
1078       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1079     if (!aPolyedre) {
1080       MESSAGE("Warning: bad volumic element");
1081       return false;
1082     }
1083     const 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     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1098   }
1099   else // other elements
1100   {
1101     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1102     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1103     if ( interlace.empty() )
1104     {
1105       std::reverse( nodes.begin(), nodes.end() ); // polygon
1106     }
1107     else if ( interlace.size() > 1 )
1108     {
1109       SMDS_MeshCell::applyInterlace( interlace, nodes );
1110     }
1111     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
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_MeshAlgos::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 ), (SMDS_MeshNode *) 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 = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1193                                                              theFaces, avoidSet,
1194                                                              &nodeInd1, &nodeInd2 )))
1195           if ( otherFace != theFace)
1196           {
1197             facesNearLink.push_back( otherFace );
1198             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1199             avoidSet.insert( otherFace );
1200           }
1201         if ( facesNearLink.size() > 1 )
1202         {
1203           // NON-MANIFOLD mesh shell !
1204           // select a face most co-directed with theFace,
1205           // other faces won't be visited this time
1206           gp_XYZ NF, NOF;
1207           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1208           double proj, maxProj = -1;
1209           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1210             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1211             if (( proj = Abs( NF * NOF )) > maxProj ) {
1212               maxProj = proj;
1213               otherFace = facesNearLink[i];
1214               nodeInd1  = nodeIndsOfFace[i].first;
1215               nodeInd2  = nodeIndsOfFace[i].second;
1216             }
1217           }
1218           // not to visit rejected faces
1219           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1220             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1221               visitedFaces.insert( facesNearLink[i] );
1222         }
1223         else if ( facesNearLink.size() == 1 )
1224         {
1225           otherFace = facesNearLink[0];
1226           nodeInd1  = nodeIndsOfFace.back().first;
1227           nodeInd2  = nodeIndsOfFace.back().second;
1228         }
1229         if ( otherFace && otherFace != theFace)
1230         {
1231           // link must be reverse in otherFace if orientation ot otherFace
1232           // is same as that of theFace
1233           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1234           {
1235             nbReori += Reorient( otherFace );
1236           }
1237           startFaces.insert( otherFace );
1238         }
1239       }
1240       std::swap( link.first, link.second ); // reverse the link
1241     }
1242   }
1243   return nbReori;
1244 }
1245
1246 //=======================================================================
1247 //function : getBadRate
1248 //purpose  :
1249 //=======================================================================
1250
1251 static double getBadRate (const SMDS_MeshElement*               theElem,
1252                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1253 {
1254   SMESH::Controls::TSequenceOfXYZ P;
1255   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1256     return 1e100;
1257   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1258   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1259 }
1260
1261 //=======================================================================
1262 //function : QuadToTri
1263 //purpose  : Cut quadrangles into triangles.
1264 //           theCrit is used to select a diagonal to cut
1265 //=======================================================================
1266
1267 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1268                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1269 {
1270   myLastCreatedElems.Clear();
1271   myLastCreatedNodes.Clear();
1272
1273   if ( !theCrit.get() )
1274     return false;
1275
1276   SMESHDS_Mesh * aMesh = GetMeshDS();
1277
1278   Handle(Geom_Surface) surface;
1279   SMESH_MesherHelper   helper( *GetMesh() );
1280
1281   TIDSortedElemSet::iterator itElem;
1282   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1283   {
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     const int aShapeId = FindShape( elem );
1304     const SMDS_MeshElement* newElem1 = 0;
1305     const SMDS_MeshElement* newElem2 = 0;
1306
1307     if ( !elem->IsQuadratic() ) // split liner quadrangle
1308     {
1309       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1310       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1311       if ( aBadRate1 <= aBadRate2 ) {
1312         // tr1 + tr2 is better
1313         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1314         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1315       }
1316       else {
1317         // tr3 + tr4 is better
1318         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1319         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1320       }
1321     }
1322     else // split quadratic quadrangle
1323     {
1324       helper.SetIsQuadratic( true );
1325       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1326
1327       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1328       if ( aNodes.size() == 9 )
1329       {
1330         helper.SetIsBiQuadratic( true );
1331         if ( aBadRate1 <= aBadRate2 )
1332           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1333         else
1334           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1335       }
1336       // create a new element
1337       if ( aBadRate1 <= aBadRate2 ) {
1338         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1339         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1340       }
1341       else {
1342         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1343         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1344       }
1345     } // quadratic case
1346
1347     // care of a new element
1348
1349     myLastCreatedElems.Append(newElem1);
1350     myLastCreatedElems.Append(newElem2);
1351     AddToSameGroups( newElem1, elem, aMesh );
1352     AddToSameGroups( newElem2, elem, aMesh );
1353
1354     // put a new triangle on the same shape
1355     if ( aShapeId )
1356       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1357     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1358
1359     aMesh->RemoveElement( elem );
1360   }
1361   return true;
1362 }
1363
1364 //=======================================================================
1365 /*!
1366  * \brief Split each of given quadrangles into 4 triangles.
1367  * \param theElems - The faces to be splitted. If empty all faces are split.
1368  */
1369 //=======================================================================
1370
1371 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1372 {
1373   myLastCreatedElems.Clear();
1374   myLastCreatedNodes.Clear();
1375
1376   SMESH_MesherHelper helper( *GetMesh() );
1377   helper.SetElementsOnShape( true );
1378
1379   SMDS_ElemIteratorPtr faceIt;
1380   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1381   else                    faceIt = elemSetIterator( theElems );
1382
1383   bool   checkUV;
1384   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1385   gp_XYZ xyz[9];
1386   vector< const SMDS_MeshNode* > nodes;
1387   SMESHDS_SubMesh*               subMeshDS;
1388   TopoDS_Face                    F;
1389   Handle(Geom_Surface)           surface;
1390   TopLoc_Location                loc;
1391
1392   while ( faceIt->more() )
1393   {
1394     const SMDS_MeshElement* quad = faceIt->next();
1395     if ( !quad || quad->NbCornerNodes() != 4 )
1396       continue;
1397
1398     // get a surface the quad is on
1399
1400     if ( quad->getshapeId() < 1 )
1401     {
1402       F.Nullify();
1403       helper.SetSubShape( 0 );
1404       subMeshDS = 0;
1405     }
1406     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1407     {
1408       helper.SetSubShape( quad->getshapeId() );
1409       if ( !helper.GetSubShape().IsNull() &&
1410            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1411       {
1412         F = TopoDS::Face( helper.GetSubShape() );
1413         surface = BRep_Tool::Surface( F, loc );
1414         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1415       }
1416       else
1417       {
1418         helper.SetSubShape( 0 );
1419         subMeshDS = 0;
1420       }
1421     }
1422
1423     // create a central node
1424
1425     const SMDS_MeshNode* nCentral;
1426     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1427
1428     if ( nodes.size() == 9 )
1429     {
1430       nCentral = nodes.back();
1431     }
1432     else
1433     {
1434       size_t iN = 0;
1435       if ( F.IsNull() )
1436       {
1437         for ( ; iN < nodes.size(); ++iN )
1438           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1439
1440         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1441           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1442
1443         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1444                                    xyz[0], xyz[1], xyz[2], xyz[3],
1445                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1446       }
1447       else
1448       {
1449         for ( ; iN < nodes.size(); ++iN )
1450           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1451
1452         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1453           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1454
1455         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1456                                   uv[0], uv[1], uv[2], uv[3],
1457                                   uv[4], uv[5], uv[6], uv[7] );
1458
1459         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1460         xyz[ 8 ] = p.XYZ();
1461       }
1462
1463       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1464                                  uv[8].X(), uv[8].Y() );
1465       myLastCreatedNodes.Append( nCentral );
1466     }
1467
1468     // create 4 triangles
1469
1470     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1471     
1472     helper.SetIsQuadratic  ( nodes.size() > 4 );
1473     helper.SetIsBiQuadratic( nodes.size() == 9 );
1474     if ( helper.GetIsQuadratic() )
1475       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1476
1477     for ( int i = 0; i < 4; ++i )
1478     {
1479       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1480                                                nodes[(i+1)%4],
1481                                                nCentral );
1482       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1483       myLastCreatedElems.Append( tria );
1484     }
1485   }
1486 }
1487
1488 //=======================================================================
1489 //function : BestSplit
1490 //purpose  : Find better diagonal for cutting.
1491 //=======================================================================
1492
1493 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1494                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1495 {
1496   myLastCreatedElems.Clear();
1497   myLastCreatedNodes.Clear();
1498
1499   if (!theCrit.get())
1500     return -1;
1501
1502   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1503     return -1;
1504
1505   if( theQuad->NbNodes()==4 ||
1506       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1507
1508     // retrieve element nodes
1509     const SMDS_MeshNode* aNodes [4];
1510     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1511     int i = 0;
1512     //while (itN->more())
1513     while (i<4) {
1514       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1515     }
1516     // compare two sets of possible triangles
1517     double aBadRate1, aBadRate2; // to what extent a set is bad
1518     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1519     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1520     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1521
1522     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1523     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1524     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1525     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1526     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1527     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1528       return 1; // diagonal 1-3
1529
1530     return 2; // diagonal 2-4
1531   }
1532   return -1;
1533 }
1534
1535 namespace
1536 {
1537   // Methods of splitting volumes into tetra
1538
1539   const int theHexTo5_1[5*4+1] =
1540     {
1541       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1542     };
1543   const int theHexTo5_2[5*4+1] =
1544     {
1545       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1546     };
1547   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1548
1549   const int theHexTo6_1[6*4+1] =
1550     {
1551       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
1552     };
1553   const int theHexTo6_2[6*4+1] =
1554     {
1555       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
1556     };
1557   const int theHexTo6_3[6*4+1] =
1558     {
1559       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
1560     };
1561   const int theHexTo6_4[6*4+1] =
1562     {
1563       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
1564     };
1565   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1566
1567   const int thePyraTo2_1[2*4+1] =
1568     {
1569       0, 1, 2, 4,    0, 2, 3, 4,   -1
1570     };
1571   const int thePyraTo2_2[2*4+1] =
1572     {
1573       1, 2, 3, 4,    1, 3, 0, 4,   -1
1574     };
1575   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1576
1577   const int thePentaTo3_1[3*4+1] =
1578     {
1579       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1580     };
1581   const int thePentaTo3_2[3*4+1] =
1582     {
1583       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1584     };
1585   const int thePentaTo3_3[3*4+1] =
1586     {
1587       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1588     };
1589   const int thePentaTo3_4[3*4+1] =
1590     {
1591       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1592     };
1593   const int thePentaTo3_5[3*4+1] =
1594     {
1595       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1596     };
1597   const int thePentaTo3_6[3*4+1] =
1598     {
1599       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1600     };
1601   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1602                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1603
1604   // Methods of splitting hexahedron into prisms
1605
1606   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1607     {
1608       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1609     };
1610   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1611     {
1612       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1613     };
1614   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1615     {
1616       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1617     };
1618
1619   const int theHexTo2Prisms_BT_1[6*2+1] =
1620     {
1621       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1622     };
1623   const int theHexTo2Prisms_BT_2[6*2+1] =
1624     {
1625       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1626     };
1627   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1628
1629   const int theHexTo2Prisms_LR_1[6*2+1] =
1630     {
1631       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1632     };
1633   const int theHexTo2Prisms_LR_2[6*2+1] =
1634     {
1635       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1636     };
1637   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1638
1639   const int theHexTo2Prisms_FB_1[6*2+1] =
1640     {
1641       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1642     };
1643   const int theHexTo2Prisms_FB_2[6*2+1] =
1644     {
1645       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1646     };
1647   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1648
1649
1650   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1651   {
1652     int _n1, _n2, _n3;
1653     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1654     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1655     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1656                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1657   };
1658   struct TSplitMethod
1659   {
1660     int        _nbSplits;
1661     int        _nbCorners;
1662     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1663     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1664     bool       _ownConn;      //!< to delete _connectivity in destructor
1665     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1666
1667     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1668       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1669     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1670     bool hasFacet( const TTriangleFacet& facet ) const
1671     {
1672       if ( _nbCorners == 4 )
1673       {
1674         const int* tetConn = _connectivity;
1675         for ( ; tetConn[0] >= 0; tetConn += 4 )
1676           if (( facet.contains( tetConn[0] ) +
1677                 facet.contains( tetConn[1] ) +
1678                 facet.contains( tetConn[2] ) +
1679                 facet.contains( tetConn[3] )) == 3 )
1680             return true;
1681       }
1682       else // prism, _nbCorners == 6
1683       {
1684         const int* prismConn = _connectivity;
1685         for ( ; prismConn[0] >= 0; prismConn += 6 )
1686         {
1687           if (( facet.contains( prismConn[0] ) &&
1688                 facet.contains( prismConn[1] ) &&
1689                 facet.contains( prismConn[2] ))
1690               ||
1691               ( facet.contains( prismConn[3] ) &&
1692                 facet.contains( prismConn[4] ) &&
1693                 facet.contains( prismConn[5] )))
1694             return true;
1695         }
1696       }
1697       return false;
1698     }
1699   };
1700
1701   //=======================================================================
1702   /*!
1703    * \brief return TSplitMethod for the given element to split into tetrahedra
1704    */
1705   //=======================================================================
1706
1707   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1708   {
1709     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1710
1711     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1712     // an edge and a face barycenter; tertaherdons are based on triangles and
1713     // a volume barycenter
1714     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1715
1716     // Find out how adjacent volumes are split
1717
1718     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1719     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1720     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1721     {
1722       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1723       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1724       if ( nbNodes < 4 ) continue;
1725
1726       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1727       const int* nInd = vol.GetFaceNodesIndices( iF );
1728       if ( nbNodes == 4 )
1729       {
1730         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1731         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1732         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1733         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1734       }
1735       else
1736       {
1737         int iCom = 0; // common node of triangle faces to split into
1738         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1739         {
1740           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1741                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1742                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1743           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1744                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1745                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1746           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1747           {
1748             triaSplits.push_back( t012 );
1749             triaSplits.push_back( t023 );
1750             break;
1751           }
1752         }
1753       }
1754       if ( !triaSplits.empty() )
1755         hasAdjacentSplits = true;
1756     }
1757
1758     // Among variants of split method select one compliant with adjacent volumes
1759
1760     TSplitMethod method;
1761     if ( !vol.Element()->IsPoly() && !is24TetMode )
1762     {
1763       int nbVariants = 2, nbTet = 0;
1764       const int** connVariants = 0;
1765       switch ( vol.Element()->GetEntityType() )
1766       {
1767       case SMDSEntity_Hexa:
1768       case SMDSEntity_Quad_Hexa:
1769       case SMDSEntity_TriQuad_Hexa:
1770         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1771           connVariants = theHexTo5, nbTet = 5;
1772         else
1773           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1774         break;
1775       case SMDSEntity_Pyramid:
1776       case SMDSEntity_Quad_Pyramid:
1777         connVariants = thePyraTo2;  nbTet = 2;
1778         break;
1779       case SMDSEntity_Penta:
1780       case SMDSEntity_Quad_Penta:
1781         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1782         break;
1783       default:
1784         nbVariants = 0;
1785       }
1786       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1787       {
1788         // check method compliancy with adjacent tetras,
1789         // all found splits must be among facets of tetras described by this method
1790         method = TSplitMethod( nbTet, connVariants[variant] );
1791         if ( hasAdjacentSplits && method._nbSplits > 0 )
1792         {
1793           bool facetCreated = true;
1794           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1795           {
1796             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1797             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1798               facetCreated = method.hasFacet( *facet );
1799           }
1800           if ( !facetCreated )
1801             method = TSplitMethod(0); // incompatible method
1802         }
1803       }
1804     }
1805     if ( method._nbSplits < 1 )
1806     {
1807       // No standard method is applicable, use a generic solution:
1808       // each facet of a volume is split into triangles and
1809       // each of triangles and a volume barycenter form a tetrahedron.
1810
1811       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1812
1813       int* connectivity = new int[ maxTetConnSize + 1 ];
1814       method._connectivity = connectivity;
1815       method._ownConn = true;
1816       method._baryNode = !isHex27; // to create central node or not
1817
1818       int connSize = 0;
1819       int baryCenInd = vol.NbNodes() - int( isHex27 );
1820       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1821       {
1822         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1823         const int*   nInd = vol.GetFaceNodesIndices( iF );
1824         // find common node of triangle facets of tetra to create
1825         int iCommon = 0; // index in linear numeration
1826         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1827         if ( !triaSplits.empty() )
1828         {
1829           // by found facets
1830           const TTriangleFacet* facet = &triaSplits.front();
1831           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1832             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1833                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1834               break;
1835         }
1836         else if ( nbNodes > 3 && !is24TetMode )
1837         {
1838           // find the best method of splitting into triangles by aspect ratio
1839           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1840           map< double, int > badness2iCommon;
1841           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1842           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1843           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1844           {
1845             double badness = 0;
1846             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1847             {
1848               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1849                                       nodes[ iQ*((iLast-1)%nbNodes)],
1850                                       nodes[ iQ*((iLast  )%nbNodes)]);
1851               badness += getBadRate( &tria, aspectRatio );
1852             }
1853             badness2iCommon.insert( make_pair( badness, iCommon ));
1854           }
1855           // use iCommon with lowest badness
1856           iCommon = badness2iCommon.begin()->second;
1857         }
1858         if ( iCommon >= nbNodes )
1859           iCommon = 0; // something wrong
1860
1861         // fill connectivity of tetrahedra based on a current face
1862         int nbTet = nbNodes - 2;
1863         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1864         {
1865           int faceBaryCenInd;
1866           if ( isHex27 )
1867           {
1868             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1869             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1870           }
1871           else
1872           {
1873             method._faceBaryNode[ iF ] = 0;
1874             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1875           }
1876           nbTet = nbNodes;
1877           for ( int i = 0; i < nbTet; ++i )
1878           {
1879             int i1 = i, i2 = (i+1) % nbNodes;
1880             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1881             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1882             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1883             connectivity[ connSize++ ] = faceBaryCenInd;
1884             connectivity[ connSize++ ] = baryCenInd;
1885           }
1886         }
1887         else
1888         {
1889           for ( int i = 0; i < nbTet; ++i )
1890           {
1891             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1892             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1893             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1894             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1895             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1896             connectivity[ connSize++ ] = baryCenInd;
1897           }
1898         }
1899         method._nbSplits += nbTet;
1900
1901       } // loop on volume faces
1902
1903       connectivity[ connSize++ ] = -1;
1904
1905     } // end of generic solution
1906
1907     return method;
1908   }
1909   //=======================================================================
1910   /*!
1911    * \brief return TSplitMethod to split haxhedron into prisms
1912    */
1913   //=======================================================================
1914
1915   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
1916                                     const int        methodFlags,
1917                                     const int        facetToSplit)
1918   {
1919     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
1920     // B, T, L, B, R, F
1921     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
1922
1923     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
1924     {
1925       static TSplitMethod to4methods[4]; // order BT, LR, FB
1926       if ( to4methods[iF]._nbSplits == 0 )
1927       {
1928         switch ( iF ) {
1929         case 0:
1930           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
1931           to4methods[iF]._faceBaryNode[ 0 ] = 0;
1932           to4methods[iF]._faceBaryNode[ 1 ] = 0;
1933           break;
1934         case 1:
1935           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
1936           to4methods[iF]._faceBaryNode[ 2 ] = 0;
1937           to4methods[iF]._faceBaryNode[ 4 ] = 0;
1938           break;
1939         case 2:
1940           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
1941           to4methods[iF]._faceBaryNode[ 3 ] = 0;
1942           to4methods[iF]._faceBaryNode[ 5 ] = 0;
1943           break;
1944         default: return to4methods[3];
1945         }
1946         to4methods[iF]._nbSplits  = 4;
1947         to4methods[iF]._nbCorners = 6;
1948       }
1949       return to4methods[iF];
1950     }
1951     // else if ( methodFlags == HEXA_TO_2_PRISMS )
1952
1953     TSplitMethod method;
1954
1955     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1956
1957     const int nbVariants = 2, nbSplits = 2;
1958     const int** connVariants = 0;
1959     switch ( iF ) {
1960     case 0: connVariants = theHexTo2Prisms_BT; break;
1961     case 1: connVariants = theHexTo2Prisms_LR; break;
1962     case 2: connVariants = theHexTo2Prisms_FB; break;
1963     default: return method;
1964     }
1965
1966     // look for prisms adjacent via facetToSplit and an opposite one
1967     for ( int is2nd = 0; is2nd < 2; ++is2nd )
1968     {
1969       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
1970       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
1971       if ( nbNodes != 4 ) return method;
1972
1973       const int* nInd = vol.GetFaceNodesIndices( iFacet );
1974       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1975       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1976       TTriangleFacet* t;
1977       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1978         t = &t012;
1979       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1980         t = &t123;
1981       else
1982         continue;
1983
1984       // there are adjacent prism
1985       for ( int variant = 0; variant < nbVariants; ++variant )
1986       {
1987         // check method compliancy with adjacent prisms,
1988         // the found prism facets must be among facets of prisms described by current method
1989         method._nbSplits     = nbSplits;
1990         method._nbCorners    = 6;
1991         method._connectivity = connVariants[ variant ];
1992         if ( method.hasFacet( *t ))
1993           return method;
1994       }
1995     }
1996
1997     // No adjacent prisms. Select a variant with a best aspect ratio.
1998
1999     double badness[2] = { 0, 0 };
2000     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2001     const SMDS_MeshNode** nodes = vol.GetNodes();
2002     for ( int variant = 0; variant < nbVariants; ++variant )
2003       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2004       {
2005         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2006         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2007
2008         method._connectivity = connVariants[ variant ];
2009         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2010         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2011         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2012
2013         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2014                                 nodes[ t->_n2 ],
2015                                 nodes[ t->_n3 ] );
2016         badness[ variant ] += getBadRate( &tria, aspectRatio );
2017       }
2018     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2019
2020     method._nbSplits     = nbSplits;
2021     method._nbCorners    = 6;
2022     method._connectivity = connVariants[ iBetter ];
2023
2024     return method;
2025   }
2026
2027   //================================================================================
2028   /*!
2029    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2030    */
2031   //================================================================================
2032
2033   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2034                                        const SMDSAbs_GeometryType geom ) const
2035   {
2036     // find the tetrahedron including the three nodes of facet
2037     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2038     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2039     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2040     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2041     while ( volIt1->more() )
2042     {
2043       const SMDS_MeshElement* v = volIt1->next();
2044       if ( v->GetGeomType() != geom )
2045         continue;
2046       const int lastCornerInd = v->NbCornerNodes() - 1;
2047       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2048         continue; // medium node not allowed
2049       const int ind2 = v->GetNodeIndex( n2 );
2050       if ( ind2 < 0 || lastCornerInd < ind2 )
2051         continue;
2052       const int ind3 = v->GetNodeIndex( n3 );
2053       if ( ind3 < 0 || lastCornerInd < ind3 )
2054         continue;
2055       return true;
2056     }
2057     return false;
2058   }
2059
2060   //=======================================================================
2061   /*!
2062    * \brief A key of a face of volume
2063    */
2064   //=======================================================================
2065
2066   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2067   {
2068     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2069     {
2070       TIDSortedNodeSet sortedNodes;
2071       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2072       int nbNodes = vol.NbFaceNodes( iF );
2073       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2074       for ( int i = 0; i < nbNodes; i += iQ )
2075         sortedNodes.insert( fNodes[i] );
2076       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2077       first.first   = (*(n++))->GetID();
2078       first.second  = (*(n++))->GetID();
2079       second.first  = (*(n++))->GetID();
2080       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2081     }
2082   };
2083 } // namespace
2084
2085 //=======================================================================
2086 //function : SplitVolumes
2087 //purpose  : Split volume elements into tetrahedra or prisms.
2088 //           If facet ID < 0, element is split into tetrahedra,
2089 //           else a hexahedron is split into prisms so that the given facet is
2090 //           split into triangles
2091 //=======================================================================
2092
2093 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2094                                      const int            theMethodFlags)
2095 {
2096   // std-like iterator on coordinates of nodes of mesh element
2097   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2098   NXyzIterator xyzEnd;
2099
2100   SMDS_VolumeTool    volTool;
2101   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2102   fHelper.ToFixNodeParameters( true );
2103
2104   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2105   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2106
2107   SMESH_SequenceOfElemPtr newNodes, newElems;
2108
2109   // map face of volume to it's baricenrtic node
2110   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2111   double bc[3];
2112
2113   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2114   for ( ; elem2facet != theElems.end(); ++elem2facet )
2115   {
2116     const SMDS_MeshElement* elem = elem2facet->first;
2117     const int       facetToSplit = elem2facet->second;
2118     if ( elem->GetType() != SMDSAbs_Volume )
2119       continue;
2120     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2121     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2122       continue;
2123
2124     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2125
2126     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2127                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2128                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2129     if ( splitMethod._nbSplits < 1 ) continue;
2130
2131     // find submesh to add new tetras to
2132     if ( !subMesh || !subMesh->Contains( elem ))
2133     {
2134       int shapeID = FindShape( elem );
2135       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2136       subMesh = GetMeshDS()->MeshElements( shapeID );
2137     }
2138     int iQ;
2139     if ( elem->IsQuadratic() )
2140     {
2141       iQ = 2;
2142       // add quadratic links to the helper
2143       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2144       {
2145         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2146         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2147         for ( int iN = 0; iN < nbN; iN += iQ )
2148           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2149       }
2150       helper.SetIsQuadratic( true );
2151     }
2152     else
2153     {
2154       iQ = 1;
2155       helper.SetIsQuadratic( false );
2156     }
2157     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2158                                         volTool.GetNodes() + elem->NbNodes() );
2159     helper.SetElementsOnShape( true );
2160     if ( splitMethod._baryNode )
2161     {
2162       // make a node at barycenter
2163       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2164       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2165       nodes.push_back( gcNode );
2166       newNodes.Append( gcNode );
2167     }
2168     if ( !splitMethod._faceBaryNode.empty() )
2169     {
2170       // make or find baricentric nodes of faces
2171       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2172       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2173       {
2174         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2175           volFace2BaryNode.insert
2176           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2177         if ( !f_n->second )
2178         {
2179           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2180           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2181         }
2182         nodes.push_back( iF_n->second = f_n->second );
2183       }
2184     }
2185
2186     // make new volumes
2187     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2188     const int* volConn = splitMethod._connectivity;
2189     if ( splitMethod._nbCorners == 4 ) // tetra
2190       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2191         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2192                                                             nodes[ volConn[1] ],
2193                                                             nodes[ volConn[2] ],
2194                                                             nodes[ volConn[3] ]));
2195     else // prisms
2196       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2197         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2198                                                             nodes[ volConn[1] ],
2199                                                             nodes[ volConn[2] ],
2200                                                             nodes[ volConn[3] ],
2201                                                             nodes[ volConn[4] ],
2202                                                             nodes[ volConn[5] ]));
2203
2204     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2205
2206     // Split faces on sides of the split volume
2207
2208     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2209     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2210     {
2211       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2212       if ( nbNodes < 4 ) continue;
2213
2214       // find an existing face
2215       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2216                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2217       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2218                                                                        /*noMedium=*/false))
2219       {
2220         // make triangles
2221         helper.SetElementsOnShape( false );
2222         vector< const SMDS_MeshElement* > triangles;
2223
2224         // find submesh to add new triangles in
2225         if ( !fSubMesh || !fSubMesh->Contains( face ))
2226         {
2227           int shapeID = FindShape( face );
2228           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2229         }
2230         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2231         if ( iF_n != splitMethod._faceBaryNode.end() )
2232         {
2233           const SMDS_MeshNode *baryNode = iF_n->second;
2234           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2235           {
2236             const SMDS_MeshNode* n1 = fNodes[iN];
2237             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2238             const SMDS_MeshNode *n3 = baryNode;
2239             if ( !volTool.IsFaceExternal( iF ))
2240               swap( n2, n3 );
2241             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2242           }
2243           if ( fSubMesh ) // update position of the bary node on geometry
2244           {
2245             if ( subMesh )
2246               subMesh->RemoveNode( baryNode, false );
2247             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2248             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2249             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2250             {
2251               fHelper.SetSubShape( s );
2252               gp_XY uv( 1e100, 1e100 );
2253               double distXYZ[4];
2254               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2255                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2256                    uv.X() < 1e100 )
2257               {
2258                 // node is too far from the surface
2259                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2260                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2261                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2262               }
2263             }
2264           }
2265         }
2266         else
2267         {
2268           // among possible triangles create ones discribed by split method
2269           const int* nInd = volTool.GetFaceNodesIndices( iF );
2270           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2271           int iCom = 0; // common node of triangle faces to split into
2272           list< TTriangleFacet > facets;
2273           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2274           {
2275             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2276                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2277                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2278             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2279                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2280                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2281             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2282             {
2283               facets.push_back( t012 );
2284               facets.push_back( t023 );
2285               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2286                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2287                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2288                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2289               break;
2290             }
2291           }
2292           list< TTriangleFacet >::iterator facet = facets.begin();
2293           if ( facet == facets.end() )
2294             break;
2295           for ( ; facet != facets.end(); ++facet )
2296           {
2297             if ( !volTool.IsFaceExternal( iF ))
2298               swap( facet->_n2, facet->_n3 );
2299             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2300                                                  volNodes[ facet->_n2 ],
2301                                                  volNodes[ facet->_n3 ]));
2302           }
2303         }
2304         for ( int i = 0; i < triangles.size(); ++i )
2305         {
2306           if ( !triangles[i] ) continue;
2307           if ( fSubMesh )
2308             fSubMesh->AddElement( triangles[i]);
2309           newElems.Append( triangles[i] );
2310         }
2311         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2312         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2313
2314       } // while a face based on facet nodes exists
2315     } // loop on volume faces to split them into triangles
2316
2317     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2318
2319     if ( geomType == SMDSEntity_TriQuad_Hexa )
2320     {
2321       // remove medium nodes that could become free
2322       for ( int i = 20; i < volTool.NbNodes(); ++i )
2323         if ( volNodes[i]->NbInverseElements() == 0 )
2324           GetMeshDS()->RemoveNode( volNodes[i] );
2325     }
2326   } // loop on volumes to split
2327   
2328   myLastCreatedNodes = newNodes;
2329   myLastCreatedElems = newElems;
2330 }
2331
2332 //=======================================================================
2333 //function : GetHexaFacetsToSplit
2334 //purpose  : For hexahedra that will be split into prisms, finds facets to
2335 //           split into triangles. Only hexahedra adjacent to the one closest
2336 //           to theFacetNormal.Location() are returned.
2337 //param [in,out] theHexas - the hexahedra
2338 //param [in]     theFacetNormal - facet normal
2339 //param [out]    theFacets - the hexahedra and found facet IDs
2340 //=======================================================================
2341
2342 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2343                                              const gp_Ax1&     theFacetNormal,
2344                                              TFacetOfElem &    theFacets)
2345 {
2346   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2347
2348   // Find a hexa closest to the location of theFacetNormal
2349
2350   const SMDS_MeshElement* startHex;
2351   {
2352     // get SMDS_ElemIteratorPtr on theHexas
2353     typedef const SMDS_MeshElement*                                      TValue;
2354     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2355     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2356     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2357     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2358     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2359       ( new TElemSetIter( theHexas.begin(),
2360                           theHexas.end(),
2361                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2362
2363     SMESH_ElementSearcher* searcher =
2364       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2365
2366     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2367
2368     delete searcher;
2369
2370     if ( !startHex )
2371       throw SALOME_Exception( THIS_METHOD "startHex not found");
2372   }
2373
2374   // Select a facet of startHex by theFacetNormal
2375
2376   SMDS_VolumeTool vTool( startHex );
2377   double norm[3], dot, maxDot = 0;
2378   int facetID = -1;
2379   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2380     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2381     {
2382       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2383       if ( dot > maxDot )
2384       {
2385         facetID = iF;
2386         maxDot = dot;
2387       }
2388     }
2389   if ( facetID < 0 )
2390     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2391
2392   // Fill theFacets starting from facetID of startHex
2393
2394   // facets used for seach of volumes adjacent to already treated ones
2395   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2396   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2397   TFacetMap facetsToCheck;
2398
2399   set<const SMDS_MeshNode*> facetNodes;
2400   const SMDS_MeshElement*   curHex;
2401
2402   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2403
2404   while ( startHex )
2405   {
2406     // move in two directions from startHex via facetID
2407     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2408     {
2409       curHex       = startHex;
2410       int curFacet = facetID;
2411       if ( is2nd ) // do not treat startHex twice
2412       {
2413         vTool.Set( curHex );
2414         if ( vTool.IsFreeFace( curFacet, &curHex ))
2415         {
2416           curHex = 0;
2417         }
2418         else
2419         {
2420           vTool.GetFaceNodes( curFacet, facetNodes );
2421           vTool.Set( curHex );
2422           curFacet = vTool.GetFaceIndex( facetNodes );
2423         }
2424       }
2425       while ( curHex )
2426       {
2427         // store a facet to split
2428         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2429         {
2430           theFacets.insert( make_pair( curHex, -1 ));
2431           break;
2432         }
2433         if ( !allHex && !theHexas.count( curHex ))
2434           break;
2435
2436         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2437           theFacets.insert( make_pair( curHex, curFacet ));
2438         if ( !facetIt2isNew.second )
2439           break;
2440
2441         // remember not-to-split facets in facetsToCheck
2442         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2443         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2444         {
2445           if ( iF == curFacet && iF == oppFacet )
2446             continue;
2447           TVolumeFaceKey facetKey ( vTool, iF );
2448           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2449           pair< TFacetMap::iterator, bool > it2isnew =
2450             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2451           if ( !it2isnew.second )
2452             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2453         }
2454         // pass to a volume adjacent via oppFacet
2455         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2456         {
2457           curHex = 0;
2458         }
2459         else
2460         {
2461           // get a new curFacet
2462           vTool.GetFaceNodes( oppFacet, facetNodes );
2463           vTool.Set( curHex );
2464           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2465         }
2466       }
2467     } // move in two directions from startHex via facetID
2468
2469     // Find a new startHex by facetsToCheck
2470
2471     startHex = 0;
2472     facetID  = -1;
2473     TFacetMap::iterator fIt = facetsToCheck.begin();
2474     while ( !startHex && fIt != facetsToCheck.end() )
2475     {
2476       const TElemFacets&  elemFacets = fIt->second;
2477       const SMDS_MeshElement*    hex = elemFacets.first->first;
2478       int                 splitFacet = elemFacets.first->second;
2479       int               lateralFacet = elemFacets.second;
2480       facetsToCheck.erase( fIt );
2481       fIt = facetsToCheck.begin();
2482
2483       vTool.Set( hex );
2484       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2485            curHex->GetGeomType() != SMDSGeom_HEXA )
2486         continue;
2487       if ( !allHex && !theHexas.count( curHex ))
2488         continue;
2489
2490       startHex = curHex;
2491
2492       // find a facet of startHex to split 
2493
2494       set<const SMDS_MeshNode*> lateralNodes;
2495       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2496       vTool.GetFaceNodes( splitFacet,   facetNodes );
2497       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2498       vTool.Set( startHex );
2499       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2500
2501       // look for a facet of startHex having common nodes with facetNodes
2502       // but not lateralFacet
2503       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2504       {
2505         if ( iF == lateralFacet )
2506           continue;
2507         int nbCommonNodes = 0;
2508         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2509         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2510           nbCommonNodes += facetNodes.count( nn[ iN ]);
2511
2512         if ( nbCommonNodes >= 2 )
2513         {
2514           facetID = iF;
2515           break;
2516         }
2517       }
2518       if ( facetID < 0 )
2519         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2520     }
2521   } //   while ( startHex )
2522 }
2523
2524 //=======================================================================
2525 //function : AddToSameGroups
2526 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2527 //=======================================================================
2528
2529 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2530                                         const SMDS_MeshElement* elemInGroups,
2531                                         SMESHDS_Mesh *          aMesh)
2532 {
2533   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2534   if (!groups.empty()) {
2535     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2536     for ( ; grIt != groups.end(); grIt++ ) {
2537       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2538       if ( group && group->Contains( elemInGroups ))
2539         group->SMDSGroup().Add( elemToAdd );
2540     }
2541   }
2542 }
2543
2544
2545 //=======================================================================
2546 //function : RemoveElemFromGroups
2547 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2548 //=======================================================================
2549 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2550                                              SMESHDS_Mesh *          aMesh)
2551 {
2552   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2553   if (!groups.empty())
2554   {
2555     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2556     for (; GrIt != groups.end(); GrIt++)
2557     {
2558       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2559       if (!grp || grp->IsEmpty()) continue;
2560       grp->SMDSGroup().Remove(removeelem);
2561     }
2562   }
2563 }
2564
2565 //================================================================================
2566 /*!
2567  * \brief Replace elemToRm by elemToAdd in the all groups
2568  */
2569 //================================================================================
2570
2571 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2572                                             const SMDS_MeshElement* elemToAdd,
2573                                             SMESHDS_Mesh *          aMesh)
2574 {
2575   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2576   if (!groups.empty()) {
2577     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2578     for ( ; grIt != groups.end(); grIt++ ) {
2579       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2580       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2581         group->SMDSGroup().Add( elemToAdd );
2582     }
2583   }
2584 }
2585
2586 //================================================================================
2587 /*!
2588  * \brief Replace elemToRm by elemToAdd in the all groups
2589  */
2590 //================================================================================
2591
2592 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2593                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2594                                             SMESHDS_Mesh *                         aMesh)
2595 {
2596   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2597   if (!groups.empty())
2598   {
2599     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2600     for ( ; grIt != groups.end(); grIt++ ) {
2601       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2602       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2603         for ( int i = 0; i < elemToAdd.size(); ++i )
2604           group->SMDSGroup().Add( elemToAdd[ i ] );
2605     }
2606   }
2607 }
2608
2609 //=======================================================================
2610 //function : QuadToTri
2611 //purpose  : Cut quadrangles into triangles.
2612 //           theCrit is used to select a diagonal to cut
2613 //=======================================================================
2614
2615 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2616                                   const bool         the13Diag)
2617 {
2618   myLastCreatedElems.Clear();
2619   myLastCreatedNodes.Clear();
2620
2621   MESSAGE( "::QuadToTri()" );
2622
2623   SMESHDS_Mesh * aMesh = GetMeshDS();
2624
2625   Handle(Geom_Surface) surface;
2626   SMESH_MesherHelper   helper( *GetMesh() );
2627
2628   TIDSortedElemSet::iterator itElem;
2629   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2630     const SMDS_MeshElement* elem = *itElem;
2631     if ( !elem || elem->GetType() != SMDSAbs_Face )
2632       continue;
2633     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2634     if(!isquad) continue;
2635
2636     if(elem->NbNodes()==4) {
2637       // retrieve element nodes
2638       const SMDS_MeshNode* aNodes [4];
2639       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2640       int i = 0;
2641       while ( itN->more() )
2642         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2643
2644       int aShapeId = FindShape( elem );
2645       const SMDS_MeshElement* newElem1 = 0;
2646       const SMDS_MeshElement* newElem2 = 0;
2647       if ( the13Diag ) {
2648         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2649         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2650       }
2651       else {
2652         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2653         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2654       }
2655       myLastCreatedElems.Append(newElem1);
2656       myLastCreatedElems.Append(newElem2);
2657       // put a new triangle on the same shape and add to the same groups
2658       if ( aShapeId )
2659         {
2660           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2661           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2662         }
2663       AddToSameGroups( newElem1, elem, aMesh );
2664       AddToSameGroups( newElem2, elem, aMesh );
2665       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2666       aMesh->RemoveElement( elem );
2667     }
2668
2669     // Quadratic quadrangle
2670
2671     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2672
2673       // get surface elem is on
2674       int aShapeId = FindShape( elem );
2675       if ( aShapeId != helper.GetSubShapeID() ) {
2676         surface.Nullify();
2677         TopoDS_Shape shape;
2678         if ( aShapeId > 0 )
2679           shape = aMesh->IndexToShape( aShapeId );
2680         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2681           TopoDS_Face face = TopoDS::Face( shape );
2682           surface = BRep_Tool::Surface( face );
2683           if ( !surface.IsNull() )
2684             helper.SetSubShape( shape );
2685         }
2686       }
2687
2688       const SMDS_MeshNode* aNodes [8];
2689       const SMDS_MeshNode* inFaceNode = 0;
2690       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2691       int i = 0;
2692       while ( itN->more() ) {
2693         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2694         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2695              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2696         {
2697           inFaceNode = aNodes[ i-1 ];
2698         }
2699       }
2700
2701       // find middle point for (0,1,2,3)
2702       // and create a node in this point;
2703       gp_XYZ p( 0,0,0 );
2704       if ( surface.IsNull() ) {
2705         for(i=0; i<4; i++)
2706           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2707         p /= 4;
2708       }
2709       else {
2710         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2711         gp_XY uv( 0,0 );
2712         for(i=0; i<4; i++)
2713           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2714         uv /= 4.;
2715         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2716       }
2717       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2718       myLastCreatedNodes.Append(newN);
2719
2720       // create a new element
2721       const SMDS_MeshElement* newElem1 = 0;
2722       const SMDS_MeshElement* newElem2 = 0;
2723       if ( the13Diag ) {
2724         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2725                                   aNodes[6], aNodes[7], newN );
2726         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2727                                   newN,      aNodes[4], aNodes[5] );
2728       }
2729       else {
2730         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2731                                   aNodes[7], aNodes[4], newN );
2732         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2733                                   newN,      aNodes[5], aNodes[6] );
2734       }
2735       myLastCreatedElems.Append(newElem1);
2736       myLastCreatedElems.Append(newElem2);
2737       // put a new triangle on the same shape and add to the same groups
2738       if ( aShapeId )
2739         {
2740           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2741           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2742         }
2743       AddToSameGroups( newElem1, elem, aMesh );
2744       AddToSameGroups( newElem2, elem, aMesh );
2745       aMesh->RemoveElement( elem );
2746     }
2747   }
2748
2749   return true;
2750 }
2751
2752 //=======================================================================
2753 //function : getAngle
2754 //purpose  :
2755 //=======================================================================
2756
2757 double getAngle(const SMDS_MeshElement * tr1,
2758                 const SMDS_MeshElement * tr2,
2759                 const SMDS_MeshNode *    n1,
2760                 const SMDS_MeshNode *    n2)
2761 {
2762   double angle = 2. * M_PI; // bad angle
2763
2764   // get normals
2765   SMESH::Controls::TSequenceOfXYZ P1, P2;
2766   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2767        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2768     return angle;
2769   gp_Vec N1,N2;
2770   if(!tr1->IsQuadratic())
2771     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2772   else
2773     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2774   if ( N1.SquareMagnitude() <= gp::Resolution() )
2775     return angle;
2776   if(!tr2->IsQuadratic())
2777     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2778   else
2779     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2780   if ( N2.SquareMagnitude() <= gp::Resolution() )
2781     return angle;
2782
2783   // find the first diagonal node n1 in the triangles:
2784   // take in account a diagonal link orientation
2785   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2786   for ( int t = 0; t < 2; t++ ) {
2787     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2788     int i = 0, iDiag = -1;
2789     while ( it->more()) {
2790       const SMDS_MeshElement *n = it->next();
2791       if ( n == n1 || n == n2 ) {
2792         if ( iDiag < 0)
2793           iDiag = i;
2794         else {
2795           if ( i - iDiag == 1 )
2796             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2797           else
2798             nFirst[ t ] = n;
2799           break;
2800         }
2801       }
2802       i++;
2803     }
2804   }
2805   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2806     N2.Reverse();
2807
2808   angle = N1.Angle( N2 );
2809   //SCRUTE( angle );
2810   return angle;
2811 }
2812
2813 // =================================================
2814 // class generating a unique ID for a pair of nodes
2815 // and able to return nodes by that ID
2816 // =================================================
2817 class LinkID_Gen {
2818 public:
2819
2820   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2821     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2822   {}
2823
2824   long GetLinkID (const SMDS_MeshNode * n1,
2825                   const SMDS_MeshNode * n2) const
2826   {
2827     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2828   }
2829
2830   bool GetNodes (const long             theLinkID,
2831                  const SMDS_MeshNode* & theNode1,
2832                  const SMDS_MeshNode* & theNode2) const
2833   {
2834     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2835     if ( !theNode1 ) return false;
2836     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2837     if ( !theNode2 ) return false;
2838     return true;
2839   }
2840
2841 private:
2842   LinkID_Gen();
2843   const SMESHDS_Mesh* myMesh;
2844   long                myMaxID;
2845 };
2846
2847
2848 //=======================================================================
2849 //function : TriToQuad
2850 //purpose  : Fuse neighbour triangles into quadrangles.
2851 //           theCrit is used to select a neighbour to fuse with.
2852 //           theMaxAngle is a max angle between element normals at which
2853 //           fusion is still performed.
2854 //=======================================================================
2855
2856 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2857                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2858                                   const double                         theMaxAngle)
2859 {
2860   myLastCreatedElems.Clear();
2861   myLastCreatedNodes.Clear();
2862
2863   MESSAGE( "::TriToQuad()" );
2864
2865   if ( !theCrit.get() )
2866     return false;
2867
2868   SMESHDS_Mesh * aMesh = GetMeshDS();
2869
2870   // Prepare data for algo: build
2871   // 1. map of elements with their linkIDs
2872   // 2. map of linkIDs with their elements
2873
2874   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2875   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2876   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2877   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2878
2879   TIDSortedElemSet::iterator itElem;
2880   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2881   {
2882     const SMDS_MeshElement* elem = *itElem;
2883     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2884     bool IsTria = ( elem->NbCornerNodes()==3 );
2885     if (!IsTria) continue;
2886
2887     // retrieve element nodes
2888     const SMDS_MeshNode* aNodes [4];
2889     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2890     int i = 0;
2891     while ( i < 3 )
2892       aNodes[ i++ ] = itN->next();
2893     aNodes[ 3 ] = aNodes[ 0 ];
2894
2895     // fill maps
2896     for ( i = 0; i < 3; i++ ) {
2897       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2898       // check if elements sharing a link can be fused
2899       itLE = mapLi_listEl.find( link );
2900       if ( itLE != mapLi_listEl.end() ) {
2901         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2902           continue;
2903         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2904         //if ( FindShape( elem ) != FindShape( elem2 ))
2905         //  continue; // do not fuse triangles laying on different shapes
2906         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2907           continue; // avoid making badly shaped quads
2908         (*itLE).second.push_back( elem );
2909       }
2910       else {
2911         mapLi_listEl[ link ].push_back( elem );
2912       }
2913       mapEl_setLi [ elem ].insert( link );
2914     }
2915   }
2916   // Clean the maps from the links shared by a sole element, ie
2917   // links to which only one element is bound in mapLi_listEl
2918
2919   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2920     int nbElems = (*itLE).second.size();
2921     if ( nbElems < 2  ) {
2922       const SMDS_MeshElement* elem = (*itLE).second.front();
2923       SMESH_TLink link = (*itLE).first;
2924       mapEl_setLi[ elem ].erase( link );
2925       if ( mapEl_setLi[ elem ].empty() )
2926         mapEl_setLi.erase( elem );
2927     }
2928   }
2929
2930   // Algo: fuse triangles into quadrangles
2931
2932   while ( ! mapEl_setLi.empty() ) {
2933     // Look for the start element:
2934     // the element having the least nb of shared links
2935     const SMDS_MeshElement* startElem = 0;
2936     int minNbLinks = 4;
2937     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2938       int nbLinks = (*itEL).second.size();
2939       if ( nbLinks < minNbLinks ) {
2940         startElem = (*itEL).first;
2941         minNbLinks = nbLinks;
2942         if ( minNbLinks == 1 )
2943           break;
2944       }
2945     }
2946
2947     // search elements to fuse starting from startElem or links of elements
2948     // fused earlyer - startLinks
2949     list< SMESH_TLink > startLinks;
2950     while ( startElem || !startLinks.empty() ) {
2951       while ( !startElem && !startLinks.empty() ) {
2952         // Get an element to start, by a link
2953         SMESH_TLink linkId = startLinks.front();
2954         startLinks.pop_front();
2955         itLE = mapLi_listEl.find( linkId );
2956         if ( itLE != mapLi_listEl.end() ) {
2957           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2958           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2959           for ( ; itE != listElem.end() ; itE++ )
2960             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2961               startElem = (*itE);
2962           mapLi_listEl.erase( itLE );
2963         }
2964       }
2965
2966       if ( startElem ) {
2967         // Get candidates to be fused
2968         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2969         const SMESH_TLink *link12, *link13;
2970         startElem = 0;
2971         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2972         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2973         ASSERT( !setLi.empty() );
2974         set< SMESH_TLink >::iterator itLi;
2975         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2976         {
2977           const SMESH_TLink & link = (*itLi);
2978           itLE = mapLi_listEl.find( link );
2979           if ( itLE == mapLi_listEl.end() )
2980             continue;
2981
2982           const SMDS_MeshElement* elem = (*itLE).second.front();
2983           if ( elem == tr1 )
2984             elem = (*itLE).second.back();
2985           mapLi_listEl.erase( itLE );
2986           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2987             continue;
2988           if ( tr2 ) {
2989             tr3 = elem;
2990             link13 = &link;
2991           }
2992           else {
2993             tr2 = elem;
2994             link12 = &link;
2995           }
2996
2997           // add other links of elem to list of links to re-start from
2998           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2999           set< SMESH_TLink >::iterator it;
3000           for ( it = links.begin(); it != links.end(); it++ ) {
3001             const SMESH_TLink& link2 = (*it);
3002             if ( link2 != link )
3003               startLinks.push_back( link2 );
3004           }
3005         }
3006
3007         // Get nodes of possible quadrangles
3008         const SMDS_MeshNode *n12 [4], *n13 [4];
3009         bool Ok12 = false, Ok13 = false;
3010         const SMDS_MeshNode *linkNode1, *linkNode2;
3011         if(tr2) {
3012           linkNode1 = link12->first;
3013           linkNode2 = link12->second;
3014           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3015             Ok12 = true;
3016         }
3017         if(tr3) {
3018           linkNode1 = link13->first;
3019           linkNode2 = link13->second;
3020           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3021             Ok13 = true;
3022         }
3023
3024         // Choose a pair to fuse
3025         if ( Ok12 && Ok13 ) {
3026           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3027           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3028           double aBadRate12 = getBadRate( &quad12, theCrit );
3029           double aBadRate13 = getBadRate( &quad13, theCrit );
3030           if (  aBadRate13 < aBadRate12 )
3031             Ok12 = false;
3032           else
3033             Ok13 = false;
3034         }
3035
3036         // Make quadrangles
3037         // and remove fused elems and remove links from the maps
3038         mapEl_setLi.erase( tr1 );
3039         if ( Ok12 )
3040         {
3041           mapEl_setLi.erase( tr2 );
3042           mapLi_listEl.erase( *link12 );
3043           if ( tr1->NbNodes() == 3 )
3044           {
3045             const SMDS_MeshElement* newElem = 0;
3046             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3047             myLastCreatedElems.Append(newElem);
3048             AddToSameGroups( newElem, tr1, aMesh );
3049             int aShapeId = tr1->getshapeId();
3050             if ( aShapeId )
3051               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3052             aMesh->RemoveElement( tr1 );
3053             aMesh->RemoveElement( tr2 );
3054           }
3055           else {
3056             vector< const SMDS_MeshNode* > N1;
3057             vector< const SMDS_MeshNode* > N2;
3058             getNodesFromTwoTria(tr1,tr2,N1,N2);
3059             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3060             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3061             // i.e. first nodes from both arrays form a new diagonal
3062             const SMDS_MeshNode* aNodes[8];
3063             aNodes[0] = N1[0];
3064             aNodes[1] = N1[1];
3065             aNodes[2] = N2[0];
3066             aNodes[3] = N2[1];
3067             aNodes[4] = N1[3];
3068             aNodes[5] = N2[5];
3069             aNodes[6] = N2[3];
3070             aNodes[7] = N1[5];
3071             const SMDS_MeshElement* newElem = 0;
3072             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3073               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3074                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3075             else
3076               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3077                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3078             myLastCreatedElems.Append(newElem);
3079             AddToSameGroups( newElem, tr1, aMesh );
3080             int aShapeId = tr1->getshapeId();
3081             if ( aShapeId )
3082               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3083             aMesh->RemoveElement( tr1 );
3084             aMesh->RemoveElement( tr2 );
3085             // remove middle node (9)
3086             if ( N1[4]->NbInverseElements() == 0 )
3087               aMesh->RemoveNode( N1[4] );
3088             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3089               aMesh->RemoveNode( N1[6] );
3090             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3091               aMesh->RemoveNode( N2[6] );
3092           }
3093         }
3094         else if ( Ok13 )
3095         {
3096           mapEl_setLi.erase( tr3 );
3097           mapLi_listEl.erase( *link13 );
3098           if ( tr1->NbNodes() == 3 ) {
3099             const SMDS_MeshElement* newElem = 0;
3100             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3101             myLastCreatedElems.Append(newElem);
3102             AddToSameGroups( newElem, tr1, aMesh );
3103             int aShapeId = tr1->getshapeId();
3104             if ( aShapeId )
3105               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3106             aMesh->RemoveElement( tr1 );
3107             aMesh->RemoveElement( tr3 );
3108           }
3109           else {
3110             vector< const SMDS_MeshNode* > N1;
3111             vector< const SMDS_MeshNode* > N2;
3112             getNodesFromTwoTria(tr1,tr3,N1,N2);
3113             // now we receive following N1 and N2 (using numeration as above image)
3114             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3115             // i.e. first nodes from both arrays form a new diagonal
3116             const SMDS_MeshNode* aNodes[8];
3117             aNodes[0] = N1[0];
3118             aNodes[1] = N1[1];
3119             aNodes[2] = N2[0];
3120             aNodes[3] = N2[1];
3121             aNodes[4] = N1[3];
3122             aNodes[5] = N2[5];
3123             aNodes[6] = N2[3];
3124             aNodes[7] = N1[5];
3125             const SMDS_MeshElement* newElem = 0;
3126             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3127               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3128                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3129             else
3130               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3131                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3132             myLastCreatedElems.Append(newElem);
3133             AddToSameGroups( newElem, tr1, aMesh );
3134             int aShapeId = tr1->getshapeId();
3135             if ( aShapeId )
3136               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3137             aMesh->RemoveElement( tr1 );
3138             aMesh->RemoveElement( tr3 );
3139             // remove middle node (9)
3140             if ( N1[4]->NbInverseElements() == 0 )
3141               aMesh->RemoveNode( N1[4] );
3142             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3143               aMesh->RemoveNode( N1[6] );
3144             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3145               aMesh->RemoveNode( N2[6] );
3146           }
3147         }
3148
3149         // Next element to fuse: the rejected one
3150         if ( tr3 )
3151           startElem = Ok12 ? tr3 : tr2;
3152
3153       } // if ( startElem )
3154     } // while ( startElem || !startLinks.empty() )
3155   } // while ( ! mapEl_setLi.empty() )
3156
3157   return true;
3158 }
3159
3160
3161 /*#define DUMPSO(txt) \
3162 //  cout << txt << endl;
3163 //=============================================================================
3164 //
3165 //
3166 //
3167 //=============================================================================
3168 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3169 {
3170 if ( i1 == i2 )
3171 return;
3172 int tmp = idNodes[ i1 ];
3173 idNodes[ i1 ] = idNodes[ i2 ];
3174 idNodes[ i2 ] = tmp;
3175 gp_Pnt Ptmp = P[ i1 ];
3176 P[ i1 ] = P[ i2 ];
3177 P[ i2 ] = Ptmp;
3178 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3179 }
3180
3181 //=======================================================================
3182 //function : SortQuadNodes
3183 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3184 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3185 //           1 or 2 else 0.
3186 //=======================================================================
3187
3188 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3189 int               idNodes[] )
3190 {
3191   gp_Pnt P[4];
3192   int i;
3193   for ( i = 0; i < 4; i++ ) {
3194     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3195     if ( !n ) return 0;
3196     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3197   }
3198
3199   gp_Vec V1(P[0], P[1]);
3200   gp_Vec V2(P[0], P[2]);
3201   gp_Vec V3(P[0], P[3]);
3202
3203   gp_Vec Cross1 = V1 ^ V2;
3204   gp_Vec Cross2 = V2 ^ V3;
3205
3206   i = 0;
3207   if (Cross1.Dot(Cross2) < 0)
3208   {
3209     Cross1 = V2 ^ V1;
3210     Cross2 = V1 ^ V3;
3211
3212     if (Cross1.Dot(Cross2) < 0)
3213       i = 2;
3214     else
3215       i = 1;
3216     swap ( i, i + 1, idNodes, P );
3217
3218     //     for ( int ii = 0; ii < 4; ii++ ) {
3219     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3220     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3221     //     }
3222   }
3223   return i;
3224 }
3225
3226 //=======================================================================
3227 //function : SortHexaNodes
3228 //purpose  : Set 8 nodes of a hexahedron in a good order.
3229 //           Return success status
3230 //=======================================================================
3231
3232 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3233                                       int               idNodes[] )
3234 {
3235   gp_Pnt P[8];
3236   int i;
3237   DUMPSO( "INPUT: ========================================");
3238   for ( i = 0; i < 8; i++ ) {
3239     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3240     if ( !n ) return false;
3241     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3242     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3243   }
3244   DUMPSO( "========================================");
3245
3246
3247   set<int> faceNodes;  // ids of bottom face nodes, to be found
3248   set<int> checkedId1; // ids of tried 2-nd nodes
3249   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3250   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3251   int iMin, iLoop1 = 0;
3252
3253   // Loop to try the 2-nd nodes
3254
3255   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3256   {
3257     // Find not checked 2-nd node
3258     for ( i = 1; i < 8; i++ )
3259       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3260         int id1 = idNodes[i];
3261         swap ( 1, i, idNodes, P );
3262         checkedId1.insert ( id1 );
3263         break;
3264       }
3265
3266     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3267     // ie that all but meybe one (id3 which is on the same face) nodes
3268     // lay on the same side from the triangle plane.
3269
3270     bool manyInPlane = false; // more than 4 nodes lay in plane
3271     int iLoop2 = 0;
3272     while ( ++iLoop2 < 6 ) {
3273
3274       // get 1-2-3 plane coeffs
3275       Standard_Real A, B, C, D;
3276       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3277       if ( N.SquareMagnitude() > gp::Resolution() )
3278       {
3279         gp_Pln pln ( P[0], N );
3280         pln.Coefficients( A, B, C, D );
3281
3282         // find the node (iMin) closest to pln
3283         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3284         set<int> idInPln;
3285         for ( i = 3; i < 8; i++ ) {
3286           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3287           if ( fabs( dist[i] ) < minDist ) {
3288             minDist = fabs( dist[i] );
3289             iMin = i;
3290           }
3291           if ( fabs( dist[i] ) <= tol )
3292             idInPln.insert( idNodes[i] );
3293         }
3294
3295         // there should not be more than 4 nodes in bottom plane
3296         if ( idInPln.size() > 1 )
3297         {
3298           DUMPSO( "### idInPln.size() = " << idInPln.size());
3299           // idInPlane does not contain the first 3 nodes
3300           if ( manyInPlane || idInPln.size() == 5)
3301             return false; // all nodes in one plane
3302           manyInPlane = true;
3303
3304           // set the 1-st node to be not in plane
3305           for ( i = 3; i < 8; i++ ) {
3306             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3307               DUMPSO( "### Reset 0-th node");
3308               swap( 0, i, idNodes, P );
3309               break;
3310             }
3311           }
3312
3313           // reset to re-check second nodes
3314           leastDist = DBL_MAX;
3315           faceNodes.clear();
3316           checkedId1.clear();
3317           iLoop1 = 0;
3318           break; // from iLoop2;
3319         }
3320
3321         // check that the other 4 nodes are on the same side
3322         bool sameSide = true;
3323         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3324         for ( i = 3; sameSide && i < 8; i++ ) {
3325           if ( i != iMin )
3326             sameSide = ( isNeg == dist[i] <= 0.);
3327         }
3328
3329         // keep best solution
3330         if ( sameSide && minDist < leastDist ) {
3331           leastDist = minDist;
3332           faceNodes.clear();
3333           faceNodes.insert( idNodes[ 1 ] );
3334           faceNodes.insert( idNodes[ 2 ] );
3335           faceNodes.insert( idNodes[ iMin ] );
3336           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3337                   << " leastDist = " << leastDist);
3338           if ( leastDist <= DBL_MIN )
3339             break;
3340         }
3341       }
3342
3343       // set next 3-d node to check
3344       int iNext = 2 + iLoop2;
3345       if ( iNext < 8 ) {
3346         DUMPSO( "Try 2-nd");
3347         swap ( 2, iNext, idNodes, P );
3348       }
3349     } // while ( iLoop2 < 6 )
3350   } // iLoop1
3351
3352   if ( faceNodes.empty() ) return false;
3353
3354   // Put the faceNodes in proper places
3355   for ( i = 4; i < 8; i++ ) {
3356     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3357       // find a place to put
3358       int iTo = 1;
3359       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3360         iTo++;
3361       DUMPSO( "Set faceNodes");
3362       swap ( iTo, i, idNodes, P );
3363     }
3364   }
3365
3366
3367   // Set nodes of the found bottom face in good order
3368   DUMPSO( " Found bottom face: ");
3369   i = SortQuadNodes( theMesh, idNodes );
3370   if ( i ) {
3371     gp_Pnt Ptmp = P[ i ];
3372     P[ i ] = P[ i+1 ];
3373     P[ i+1 ] = Ptmp;
3374   }
3375   //   else
3376   //     for ( int ii = 0; ii < 4; ii++ ) {
3377   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3378   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3379   //    }
3380
3381   // Gravity center of the top and bottom faces
3382   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3383   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3384
3385   // Get direction from the bottom to the top face
3386   gp_Vec upDir ( aGCb, aGCt );
3387   Standard_Real upDirSize = upDir.Magnitude();
3388   if ( upDirSize <= gp::Resolution() ) return false;
3389   upDir / upDirSize;
3390
3391   // Assure that the bottom face normal points up
3392   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3393   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3394   if ( Nb.Dot( upDir ) < 0 ) {
3395     DUMPSO( "Reverse bottom face");
3396     swap( 1, 3, idNodes, P );
3397   }
3398
3399   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3400   Standard_Real minDist = DBL_MAX;
3401   for ( i = 4; i < 8; i++ ) {
3402     // projection of P[i] to the plane defined by P[0] and upDir
3403     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3404     Standard_Real sqDist = P[0].SquareDistance( Pp );
3405     if ( sqDist < minDist ) {
3406       minDist = sqDist;
3407       iMin = i;
3408     }
3409   }
3410   DUMPSO( "Set 4-th");
3411   swap ( 4, iMin, idNodes, P );
3412
3413   // Set nodes of the top face in good order
3414   DUMPSO( "Sort top face");
3415   i = SortQuadNodes( theMesh, &idNodes[4] );
3416   if ( i ) {
3417     i += 4;
3418     gp_Pnt Ptmp = P[ i ];
3419     P[ i ] = P[ i+1 ];
3420     P[ i+1 ] = Ptmp;
3421   }
3422
3423   // Assure that direction of the top face normal is from the bottom face
3424   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3425   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3426   if ( Nt.Dot( upDir ) < 0 ) {
3427     DUMPSO( "Reverse top face");
3428     swap( 5, 7, idNodes, P );
3429   }
3430
3431   //   DUMPSO( "OUTPUT: ========================================");
3432   //   for ( i = 0; i < 8; i++ ) {
3433   //     float *p = ugrid->GetPoint(idNodes[i]);
3434   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3435   //   }
3436
3437   return true;
3438 }*/
3439
3440 //================================================================================
3441 /*!
3442  * \brief Return nodes linked to the given one
3443  * \param theNode - the node
3444  * \param linkedNodes - the found nodes
3445  * \param type - the type of elements to check
3446  *
3447  * Medium nodes are ignored
3448  */
3449 //================================================================================
3450
3451 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3452                                        TIDSortedElemSet &   linkedNodes,
3453                                        SMDSAbs_ElementType  type )
3454 {
3455   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3456   while ( elemIt->more() )
3457   {
3458     const SMDS_MeshElement* elem = elemIt->next();
3459     if(elem->GetType() == SMDSAbs_0DElement)
3460       continue;
3461
3462     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3463     if ( elem->GetType() == SMDSAbs_Volume )
3464     {
3465       SMDS_VolumeTool vol( elem );
3466       while ( nodeIt->more() ) {
3467         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3468         if ( theNode != n && vol.IsLinked( theNode, n ))
3469           linkedNodes.insert( n );
3470       }
3471     }
3472     else
3473     {
3474       for ( int i = 0; nodeIt->more(); ++i ) {
3475         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3476         if ( n == theNode ) {
3477           int iBefore = i - 1;
3478           int iAfter  = i + 1;
3479           if ( elem->IsQuadratic() ) {
3480             int nb = elem->NbNodes() / 2;
3481             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3482             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3483           }
3484           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3485           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3486         }
3487       }
3488     }
3489   }
3490 }
3491
3492 //=======================================================================
3493 //function : laplacianSmooth
3494 //purpose  : pulls theNode toward the center of surrounding nodes directly
3495 //           connected to that node along an element edge
3496 //=======================================================================
3497
3498 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3499                      const Handle(Geom_Surface)&          theSurface,
3500                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3501 {
3502   // find surrounding nodes
3503
3504   TIDSortedElemSet nodeSet;
3505   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3506
3507   // compute new coodrs
3508
3509   double coord[] = { 0., 0., 0. };
3510   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3511   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3512     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3513     if ( theSurface.IsNull() ) { // smooth in 3D
3514       coord[0] += node->X();
3515       coord[1] += node->Y();
3516       coord[2] += node->Z();
3517     }
3518     else { // smooth in 2D
3519       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3520       gp_XY* uv = theUVMap[ node ];
3521       coord[0] += uv->X();
3522       coord[1] += uv->Y();
3523     }
3524   }
3525   int nbNodes = nodeSet.size();
3526   if ( !nbNodes )
3527     return;
3528   coord[0] /= nbNodes;
3529   coord[1] /= nbNodes;
3530
3531   if ( !theSurface.IsNull() ) {
3532     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3533     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3534     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3535     coord[0] = p3d.X();
3536     coord[1] = p3d.Y();
3537     coord[2] = p3d.Z();
3538   }
3539   else
3540     coord[2] /= nbNodes;
3541
3542   // move node
3543
3544   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3545 }
3546
3547 //=======================================================================
3548 //function : centroidalSmooth
3549 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3550 //           surrounding elements
3551 //=======================================================================
3552
3553 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3554                       const Handle(Geom_Surface)&          theSurface,
3555                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3556 {
3557   gp_XYZ aNewXYZ(0.,0.,0.);
3558   SMESH::Controls::Area anAreaFunc;
3559   double totalArea = 0.;
3560   int nbElems = 0;
3561
3562   // compute new XYZ
3563
3564   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3565   while ( elemIt->more() )
3566   {
3567     const SMDS_MeshElement* elem = elemIt->next();
3568     nbElems++;
3569
3570     gp_XYZ elemCenter(0.,0.,0.);
3571     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3572     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3573     int nn = elem->NbNodes();
3574     if(elem->IsQuadratic()) nn = nn/2;
3575     int i=0;
3576     //while ( itN->more() ) {
3577     while ( i<nn ) {
3578       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3579       i++;
3580       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3581       aNodePoints.push_back( aP );
3582       if ( !theSurface.IsNull() ) { // smooth in 2D
3583         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3584         gp_XY* uv = theUVMap[ aNode ];
3585         aP.SetCoord( uv->X(), uv->Y(), 0. );
3586       }
3587       elemCenter += aP;
3588     }
3589     double elemArea = anAreaFunc.GetValue( aNodePoints );
3590     totalArea += elemArea;
3591     elemCenter /= nn;
3592     aNewXYZ += elemCenter * elemArea;
3593   }
3594   aNewXYZ /= totalArea;
3595   if ( !theSurface.IsNull() ) {
3596     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3597     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3598   }
3599
3600   // move node
3601
3602   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3603 }
3604
3605 //=======================================================================
3606 //function : getClosestUV
3607 //purpose  : return UV of closest projection
3608 //=======================================================================
3609
3610 static bool getClosestUV (Extrema_GenExtPS& projector,
3611                           const gp_Pnt&     point,
3612                           gp_XY &           result)
3613 {
3614   projector.Perform( point );
3615   if ( projector.IsDone() ) {
3616     double u, v, minVal = DBL_MAX;
3617     for ( int i = projector.NbExt(); i > 0; i-- )
3618 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3619       if ( projector.SquareDistance( i ) < minVal ) {
3620         minVal = projector.SquareDistance( i );
3621 #else
3622       if ( projector.Value( i ) < minVal ) {
3623         minVal = projector.Value( i );
3624 #endif
3625         projector.Point( i ).Parameter( u, v );
3626       }
3627     result.SetCoord( u, v );
3628     return true;
3629   }
3630   return false;
3631 }
3632
3633 //=======================================================================
3634 //function : Smooth
3635 //purpose  : Smooth theElements during theNbIterations or until a worst
3636 //           element has aspect ratio <= theTgtAspectRatio.
3637 //           Aspect Ratio varies in range [1.0, inf].
3638 //           If theElements is empty, the whole mesh is smoothed.
3639 //           theFixedNodes contains additionally fixed nodes. Nodes built
3640 //           on edges and boundary nodes are always fixed.
3641 //=======================================================================
3642
3643 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3644                                set<const SMDS_MeshNode*> & theFixedNodes,
3645                                const SmoothMethod          theSmoothMethod,
3646                                const int                   theNbIterations,
3647                                double                      theTgtAspectRatio,
3648                                const bool                  the2D)
3649 {
3650   myLastCreatedElems.Clear();
3651   myLastCreatedNodes.Clear();
3652
3653   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3654
3655   if ( theTgtAspectRatio < 1.0 )
3656     theTgtAspectRatio = 1.0;
3657
3658   const double disttol = 1.e-16;
3659
3660   SMESH::Controls::AspectRatio aQualityFunc;
3661
3662   SMESHDS_Mesh* aMesh = GetMeshDS();
3663
3664   if ( theElems.empty() ) {
3665     // add all faces to theElems
3666     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3667     while ( fIt->more() ) {
3668       const SMDS_MeshElement* face = fIt->next();
3669       theElems.insert( theElems.end(), face );
3670     }
3671   }
3672   // get all face ids theElems are on
3673   set< int > faceIdSet;
3674   TIDSortedElemSet::iterator itElem;
3675   if ( the2D )
3676     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3677       int fId = FindShape( *itElem );
3678       // check that corresponding submesh exists and a shape is face
3679       if (fId &&
3680           faceIdSet.find( fId ) == faceIdSet.end() &&
3681           aMesh->MeshElements( fId )) {
3682         TopoDS_Shape F = aMesh->IndexToShape( fId );
3683         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3684           faceIdSet.insert( fId );
3685       }
3686     }
3687   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3688
3689   // ===============================================
3690   // smooth elements on each TopoDS_Face separately
3691   // ===============================================
3692
3693   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3694   for ( ; fId != faceIdSet.rend(); ++fId ) {
3695     // get face surface and submesh
3696     Handle(Geom_Surface) surface;
3697     SMESHDS_SubMesh* faceSubMesh = 0;
3698     TopoDS_Face face;
3699     double fToler2 = 0, f,l;
3700     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3701     bool isUPeriodic = false, isVPeriodic = false;
3702     if ( *fId ) {
3703       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3704       surface = BRep_Tool::Surface( face );
3705       faceSubMesh = aMesh->MeshElements( *fId );
3706       fToler2 = BRep_Tool::Tolerance( face );
3707       fToler2 *= fToler2 * 10.;
3708       isUPeriodic = surface->IsUPeriodic();
3709       if ( isUPeriodic )
3710         surface->UPeriod();
3711       isVPeriodic = surface->IsVPeriodic();
3712       if ( isVPeriodic )
3713         surface->VPeriod();
3714       surface->Bounds( u1, u2, v1, v2 );
3715     }
3716     // ---------------------------------------------------------
3717     // for elements on a face, find movable and fixed nodes and
3718     // compute UV for them
3719     // ---------------------------------------------------------
3720     bool checkBoundaryNodes = false;
3721     bool isQuadratic = false;
3722     set<const SMDS_MeshNode*> setMovableNodes;
3723     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3724     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3725     list< const SMDS_MeshElement* > elemsOnFace;
3726
3727     Extrema_GenExtPS projector;
3728     GeomAdaptor_Surface surfAdaptor;
3729     if ( !surface.IsNull() ) {
3730       surfAdaptor.Load( surface );
3731       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3732     }
3733     int nbElemOnFace = 0;
3734     itElem = theElems.begin();
3735     // loop on not yet smoothed elements: look for elems on a face
3736     while ( itElem != theElems.end() ) {
3737       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3738         break; // all elements found
3739
3740       const SMDS_MeshElement* elem = *itElem;
3741       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3742            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3743         ++itElem;
3744         continue;
3745       }
3746       elemsOnFace.push_back( elem );
3747       theElems.erase( itElem++ );
3748       nbElemOnFace++;
3749
3750       if ( !isQuadratic )
3751         isQuadratic = elem->IsQuadratic();
3752
3753       // get movable nodes of elem
3754       const SMDS_MeshNode* node;
3755       SMDS_TypeOfPosition posType;
3756       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3757       int nn = 0, nbn =  elem->NbNodes();
3758       if(elem->IsQuadratic())
3759         nbn = nbn/2;
3760       while ( nn++ < nbn ) {
3761         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3762         const SMDS_PositionPtr& pos = node->GetPosition();
3763         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3764         if (posType != SMDS_TOP_EDGE &&
3765             posType != SMDS_TOP_VERTEX &&
3766             theFixedNodes.find( node ) == theFixedNodes.end())
3767         {
3768           // check if all faces around the node are on faceSubMesh
3769           // because a node on edge may be bound to face
3770           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3771           bool all = true;
3772           if ( faceSubMesh ) {
3773             while ( eIt->more() && all ) {
3774               const SMDS_MeshElement* e = eIt->next();
3775               all = faceSubMesh->Contains( e );
3776             }
3777           }
3778           if ( all )
3779             setMovableNodes.insert( node );
3780           else
3781             checkBoundaryNodes = true;
3782         }
3783         if ( posType == SMDS_TOP_3DSPACE )
3784           checkBoundaryNodes = true;
3785       }
3786
3787       if ( surface.IsNull() )
3788         continue;
3789
3790       // get nodes to check UV
3791       list< const SMDS_MeshNode* > uvCheckNodes;
3792       itN = elem->nodesIterator();
3793       nn = 0; nbn =  elem->NbNodes();
3794       if(elem->IsQuadratic())
3795         nbn = nbn/2;
3796       while ( nn++ < nbn ) {
3797         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3798         if ( uvMap.find( node ) == uvMap.end() )
3799           uvCheckNodes.push_back( node );
3800         // add nodes of elems sharing node
3801         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3802         //         while ( eIt->more() ) {
3803         //           const SMDS_MeshElement* e = eIt->next();
3804         //           if ( e != elem ) {
3805         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3806         //             while ( nIt->more() ) {
3807         //               const SMDS_MeshNode* n =
3808         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3809         //               if ( uvMap.find( n ) == uvMap.end() )
3810         //                 uvCheckNodes.push_back( n );
3811         //             }
3812         //           }
3813         //         }
3814       }
3815       // check UV on face
3816       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3817       for ( ; n != uvCheckNodes.end(); ++n ) {
3818         node = *n;
3819         gp_XY uv( 0, 0 );
3820         const SMDS_PositionPtr& pos = node->GetPosition();
3821         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3822         // get existing UV
3823         switch ( posType ) {
3824         case SMDS_TOP_FACE: {
3825           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3826           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3827           break;
3828         }
3829         case SMDS_TOP_EDGE: {
3830           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3831           Handle(Geom2d_Curve) pcurve;
3832           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3833             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3834           if ( !pcurve.IsNull() ) {
3835             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3836             uv = pcurve->Value( u ).XY();
3837           }
3838           break;
3839         }
3840         case SMDS_TOP_VERTEX: {
3841           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3842           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3843             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3844           break;
3845         }
3846         default:;
3847         }
3848         // check existing UV
3849         bool project = true;
3850         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3851         double dist1 = DBL_MAX, dist2 = 0;
3852         if ( posType != SMDS_TOP_3DSPACE ) {
3853           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3854           project = dist1 > fToler2;
3855         }
3856         if ( project ) { // compute new UV
3857           gp_XY newUV;
3858           if ( !getClosestUV( projector, pNode, newUV )) {
3859             MESSAGE("Node Projection Failed " << node);
3860           }
3861           else {
3862             if ( isUPeriodic )
3863               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3864             if ( isVPeriodic )
3865               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3866             // check new UV
3867             if ( posType != SMDS_TOP_3DSPACE )
3868               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3869             if ( dist2 < dist1 )
3870               uv = newUV;
3871           }
3872         }
3873         // store UV in the map
3874         listUV.push_back( uv );
3875         uvMap.insert( make_pair( node, &listUV.back() ));
3876       }
3877     } // loop on not yet smoothed elements
3878
3879     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3880       checkBoundaryNodes = true;
3881
3882     // fix nodes on mesh boundary
3883
3884     if ( checkBoundaryNodes ) {
3885       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3886       map< SMESH_TLink, int >::iterator link_nb;
3887       // put all elements links to linkNbMap
3888       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3889       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3890         const SMDS_MeshElement* elem = (*elemIt);
3891         int nbn =  elem->NbCornerNodes();
3892         // loop on elem links: insert them in linkNbMap
3893         for ( int iN = 0; iN < nbn; ++iN ) {
3894           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3895           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3896           SMESH_TLink link( n1, n2 );
3897           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3898           link_nb->second++;
3899         }
3900       }
3901       // remove nodes that are in links encountered only once from setMovableNodes
3902       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3903         if ( link_nb->second == 1 ) {
3904           setMovableNodes.erase( link_nb->first.node1() );
3905           setMovableNodes.erase( link_nb->first.node2() );
3906         }
3907       }
3908     }
3909
3910     // -----------------------------------------------------
3911     // for nodes on seam edge, compute one more UV ( uvMap2 );
3912     // find movable nodes linked to nodes on seam and which
3913     // are to be smoothed using the second UV ( uvMap2 )
3914     // -----------------------------------------------------
3915
3916     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3917     if ( !surface.IsNull() ) {
3918       TopExp_Explorer eExp( face, TopAbs_EDGE );
3919       for ( ; eExp.More(); eExp.Next() ) {
3920         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3921         if ( !BRep_Tool::IsClosed( edge, face ))
3922           continue;
3923         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3924         if ( !sm ) continue;
3925         // find out which parameter varies for a node on seam
3926         double f,l;
3927         gp_Pnt2d uv1, uv2;
3928         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3929         if ( pcurve.IsNull() ) continue;
3930         uv1 = pcurve->Value( f );
3931         edge.Reverse();
3932         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3933         if ( pcurve.IsNull() ) continue;
3934         uv2 = pcurve->Value( f );
3935         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3936         // assure uv1 < uv2
3937         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3938           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3939         }
3940         // get nodes on seam and its vertices
3941         list< const SMDS_MeshNode* > seamNodes;
3942         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3943         while ( nSeamIt->more() ) {
3944           const SMDS_MeshNode* node = nSeamIt->next();
3945           if ( !isQuadratic || !IsMedium( node ))
3946             seamNodes.push_back( node );
3947         }
3948         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3949         for ( ; vExp.More(); vExp.Next() ) {
3950           sm = aMesh->MeshElements( vExp.Current() );
3951           if ( sm ) {
3952             nSeamIt = sm->GetNodes();
3953             while ( nSeamIt->more() )
3954               seamNodes.push_back( nSeamIt->next() );
3955           }
3956         }
3957         // loop on nodes on seam
3958         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3959         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3960           const SMDS_MeshNode* nSeam = *noSeIt;
3961           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3962           if ( n_uv == uvMap.end() )
3963             continue;
3964           // set the first UV
3965           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3966           // set the second UV
3967           listUV.push_back( *n_uv->second );
3968           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3969           if ( uvMap2.empty() )
3970             uvMap2 = uvMap; // copy the uvMap contents
3971           uvMap2[ nSeam ] = &listUV.back();
3972
3973           // collect movable nodes linked to ones on seam in nodesNearSeam
3974           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3975           while ( eIt->more() ) {
3976             const SMDS_MeshElement* e = eIt->next();
3977             int nbUseMap1 = 0, nbUseMap2 = 0;
3978             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3979             int nn = 0, nbn =  e->NbNodes();
3980             if(e->IsQuadratic()) nbn = nbn/2;
3981             while ( nn++ < nbn )
3982             {
3983               const SMDS_MeshNode* n =
3984                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3985               if (n == nSeam ||
3986                   setMovableNodes.find( n ) == setMovableNodes.end() )
3987                 continue;
3988               // add only nodes being closer to uv2 than to uv1
3989               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3990                            0.5 * ( n->Y() + nSeam->Y() ),
3991                            0.5 * ( n->Z() + nSeam->Z() ));
3992               gp_XY uv;
3993               getClosestUV( projector, pMid, uv );
3994               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3995                 nodesNearSeam.insert( n );
3996                 nbUseMap2++;
3997               }
3998               else
3999                 nbUseMap1++;
4000             }
4001             // for centroidalSmooth all element nodes must
4002             // be on one side of a seam
4003             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4004               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4005               nn = 0;
4006               while ( nn++ < nbn ) {
4007                 const SMDS_MeshNode* n =
4008                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4009                 setMovableNodes.erase( n );
4010               }
4011             }
4012           }
4013         } // loop on nodes on seam
4014       } // loop on edge of a face
4015     } // if ( !face.IsNull() )
4016
4017     if ( setMovableNodes.empty() ) {
4018       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4019       continue; // goto next face
4020     }
4021
4022     // -------------
4023     // SMOOTHING //
4024     // -------------
4025
4026     int it = -1;
4027     double maxRatio = -1., maxDisplacement = -1.;
4028     set<const SMDS_MeshNode*>::iterator nodeToMove;
4029     for ( it = 0; it < theNbIterations; it++ ) {
4030       maxDisplacement = 0.;
4031       nodeToMove = setMovableNodes.begin();
4032       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4033         const SMDS_MeshNode* node = (*nodeToMove);
4034         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4035
4036         // smooth
4037         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4038         if ( theSmoothMethod == LAPLACIAN )
4039           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4040         else
4041           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4042
4043         // node displacement
4044         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4045         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4046         if ( aDispl > maxDisplacement )
4047           maxDisplacement = aDispl;
4048       }
4049       // no node movement => exit
4050       //if ( maxDisplacement < 1.e-16 ) {
4051       if ( maxDisplacement < disttol ) {
4052         MESSAGE("-- no node movement --");
4053         break;
4054       }
4055
4056       // check elements quality
4057       maxRatio  = 0;
4058       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4059       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4060         const SMDS_MeshElement* elem = (*elemIt);
4061         if ( !elem || elem->GetType() != SMDSAbs_Face )
4062           continue;
4063         SMESH::Controls::TSequenceOfXYZ aPoints;
4064         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4065           double aValue = aQualityFunc.GetValue( aPoints );
4066           if ( aValue > maxRatio )
4067             maxRatio = aValue;
4068         }
4069       }
4070       if ( maxRatio <= theTgtAspectRatio ) {
4071         MESSAGE("-- quality achived --");
4072         break;
4073       }
4074       if (it+1 == theNbIterations) {
4075         MESSAGE("-- Iteration limit exceeded --");
4076       }
4077     } // smoothing iterations
4078
4079     MESSAGE(" Face id: " << *fId <<
4080             " Nb iterstions: " << it <<
4081             " Displacement: " << maxDisplacement <<
4082             " Aspect Ratio " << maxRatio);
4083
4084     // ---------------------------------------
4085     // new nodes positions are computed,
4086     // record movement in DS and set new UV
4087     // ---------------------------------------
4088     nodeToMove = setMovableNodes.begin();
4089     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4090       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4091       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4092       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4093       if ( node_uv != uvMap.end() ) {
4094         gp_XY* uv = node_uv->second;
4095         node->SetPosition
4096           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4097       }
4098     }
4099
4100     // move medium nodes of quadratic elements
4101     if ( isQuadratic )
4102     {
4103       SMESH_MesherHelper helper( *GetMesh() );
4104       helper.SetSubShape( face );
4105       vector<const SMDS_MeshNode*> nodes;
4106       bool checkUV;
4107       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4108       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4109       {
4110         const SMDS_MeshElement* QF = *elemIt;
4111         if ( QF->IsQuadratic() )
4112         {
4113           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4114                         SMDS_MeshElement::iterator() );
4115           nodes.push_back( nodes[0] );
4116           gp_Pnt xyz;
4117           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4118           {
4119             if ( !surface.IsNull() )
4120             {
4121               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4122               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4123               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4124               xyz = surface->Value( uv.X(), uv.Y() );
4125             }
4126             else {
4127               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4128             }
4129             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4130               // we have to move a medium node
4131               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4132           }
4133         }
4134       }
4135     }
4136
4137   } // loop on face ids
4138
4139 }
4140
4141 //=======================================================================
4142 //function : isReverse
4143 //purpose  : Return true if normal of prevNodes is not co-directied with
4144 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4145 //           iNotSame is where prevNodes and nextNodes are different.
4146 //           If result is true then future volume orientation is OK
4147 //=======================================================================
4148
4149 static bool isReverse(const SMDS_MeshElement*             face,
4150                       const vector<const SMDS_MeshNode*>& prevNodes,
4151                       const vector<const SMDS_MeshNode*>& nextNodes,
4152                       const int                           iNotSame)
4153 {
4154
4155   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4156   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4157   gp_XYZ extrDir( pN - pP ), faceNorm;
4158   SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4159
4160   return faceNorm * extrDir < 0.0;
4161 }
4162
4163 //=======================================================================
4164 /*!
4165  * \brief Create elements by sweeping an element
4166  * \param elem - element to sweep
4167  * \param newNodesItVec - nodes generated from each node of the element
4168  * \param newElems - generated elements
4169  * \param nbSteps - number of sweeping steps
4170  * \param srcElements - to append elem for each generated element
4171  */
4172 //=======================================================================
4173
4174 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4175                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4176                                     list<const SMDS_MeshElement*>&        newElems,
4177                                     const int                             nbSteps,
4178                                     SMESH_SequenceOfElemPtr&              srcElements)
4179 {
4180   //MESSAGE("sweepElement " << nbSteps);
4181   SMESHDS_Mesh* aMesh = GetMeshDS();
4182
4183   const int           nbNodes = elem->NbNodes();
4184   const int         nbCorners = elem->NbCornerNodes();
4185   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4186                                                           polyhedron creation !!! */
4187   // Loop on elem nodes:
4188   // find new nodes and detect same nodes indices
4189   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4190   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4191   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4192   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4193
4194   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4195   vector<int> sames(nbNodes);
4196   vector<bool> isSingleNode(nbNodes);
4197
4198   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4199     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4200     const SMDS_MeshNode*                         node = nnIt->first;
4201     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4202     if ( listNewNodes.empty() )
4203       return;
4204
4205     itNN   [ iNode ] = listNewNodes.begin();
4206     prevNod[ iNode ] = node;
4207     nextNod[ iNode ] = listNewNodes.front();
4208
4209     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4210                                                              corner node of linear */
4211     if ( prevNod[ iNode ] != nextNod [ iNode ])
4212       nbDouble += !isSingleNode[iNode];
4213
4214     if( iNode < nbCorners ) { // check corners only
4215       if ( prevNod[ iNode ] == nextNod [ iNode ])
4216         sames[nbSame++] = iNode;
4217       else
4218         iNotSameNode = iNode;
4219     }
4220   }
4221
4222   if ( nbSame == nbNodes || nbSame > 2) {
4223     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4224     return;
4225   }
4226
4227   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4228   {
4229     // fix nodes order to have bottom normal external
4230     if ( baseType == SMDSEntity_Polygon )
4231     {
4232       std::reverse( itNN.begin(), itNN.end() );
4233       std::reverse( prevNod.begin(), prevNod.end() );
4234       std::reverse( midlNod.begin(), midlNod.end() );
4235       std::reverse( nextNod.begin(), nextNod.end() );
4236       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4237     }
4238     else
4239     {
4240       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4241       SMDS_MeshCell::applyInterlace( ind, itNN );
4242       SMDS_MeshCell::applyInterlace( ind, prevNod );
4243       SMDS_MeshCell::applyInterlace( ind, nextNod );
4244       SMDS_MeshCell::applyInterlace( ind, midlNod );
4245       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4246       if ( nbSame > 0 )
4247       {
4248         sames[nbSame] = iNotSameNode;
4249         for ( int j = 0; j <= nbSame; ++j )
4250           for ( size_t i = 0; i < ind.size(); ++i )
4251             if ( ind[i] == sames[j] )
4252             {
4253               sames[j] = i;
4254               break;
4255             }
4256         iNotSameNode = sames[nbSame];
4257       }
4258     }
4259   }
4260
4261   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4262   if ( nbSame > 0 ) {
4263     iSameNode    = sames[ nbSame-1 ];
4264     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4265     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4266     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4267   }
4268
4269   // make new elements
4270   for (int iStep = 0; iStep < nbSteps; iStep++ )
4271   {
4272     // get next nodes
4273     for ( iNode = 0; iNode < nbNodes; iNode++ )
4274     {
4275       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4276       nextNod[ iNode ] = *itNN[ iNode ]++;
4277     }
4278
4279     SMDS_MeshElement* aNewElem = 0;
4280     /*if(!elem->IsPoly())*/ {
4281       switch ( baseType ) {
4282       case SMDSEntity_0D:
4283       case SMDSEntity_Node: { // sweep NODE
4284         if ( nbSame == 0 ) {
4285           if ( isSingleNode[0] )
4286             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4287           else
4288             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4289         }
4290         else
4291           return;
4292         break;
4293       }
4294       case SMDSEntity_Edge: { // sweep EDGE
4295         if ( nbDouble == 0 )
4296         {
4297           if ( nbSame == 0 ) // ---> quadrangle
4298             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4299                                       nextNod[ 1 ], nextNod[ 0 ] );
4300           else               // ---> triangle
4301             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4302                                       nextNod[ iNotSameNode ] );
4303         }
4304         else                 // ---> polygon
4305         {
4306           vector<const SMDS_MeshNode*> poly_nodes;
4307           poly_nodes.push_back( prevNod[0] );
4308           poly_nodes.push_back( prevNod[1] );
4309           if ( prevNod[1] != nextNod[1] )
4310           {
4311             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4312             poly_nodes.push_back( nextNod[1] );
4313           }
4314           if ( prevNod[0] != nextNod[0] )
4315           {
4316             poly_nodes.push_back( nextNod[0] );
4317             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4318           }
4319           switch ( poly_nodes.size() ) {
4320           case 3:
4321             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4322             break;
4323           case 4:
4324             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4325                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4326             break;
4327           default:
4328             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4329           }
4330         }
4331         break;
4332       }
4333       case SMDSEntity_Triangle: // TRIANGLE --->
4334         {
4335           if ( nbDouble > 0 ) break;
4336           if ( nbSame == 0 )       // ---> pentahedron
4337             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4338                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4339
4340           else if ( nbSame == 1 )  // ---> pyramid
4341             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4342                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4343                                          nextNod[ iSameNode ]);
4344
4345           else // 2 same nodes:       ---> tetrahedron
4346             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4347                                          nextNod[ iNotSameNode ]);
4348           break;
4349         }
4350       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4351         {
4352           if ( nbSame == 2 )
4353             return;
4354           if ( nbDouble+nbSame == 2 )
4355           {
4356             if(nbSame==0) {      // ---> quadratic quadrangle
4357               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4358                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4359             }
4360             else { //(nbSame==1) // ---> quadratic triangle
4361               if(sames[0]==2) {
4362                 return; // medium node on axis
4363               }
4364               else if(sames[0]==0)
4365                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4366                                           nextNod[2], midlNod[1], prevNod[2]);
4367               else // sames[0]==1
4368                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4369                                           midlNod[0], nextNod[2], prevNod[2]);
4370             }
4371           }
4372           else if ( nbDouble == 3 )
4373           {
4374             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4375               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4376                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4377             }
4378           }
4379           else
4380             return;
4381           break;
4382         }
4383       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4384         if ( nbDouble > 0 ) break;
4385
4386         if ( nbSame == 0 )       // ---> hexahedron
4387           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4388                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4389
4390         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4391           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4392                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4393                                        nextNod[ iSameNode ]);
4394           newElems.push_back( aNewElem );
4395           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4396                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4397                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4398         }
4399         else if ( nbSame == 2 ) { // ---> pentahedron
4400           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4401             // iBeforeSame is same too
4402             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4403                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4404                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4405           else
4406             // iAfterSame is same too
4407             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4408                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4409                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4410         }
4411         break;
4412       }
4413       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4414       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4415         if ( nbDouble+nbSame != 3 ) break;
4416         if(nbSame==0) {
4417           // --->  pentahedron with 15 nodes
4418           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4419                                        nextNod[0], nextNod[1], nextNod[2],
4420                                        prevNod[3], prevNod[4], prevNod[5],
4421                                        nextNod[3], nextNod[4], nextNod[5],
4422                                        midlNod[0], midlNod[1], midlNod[2]);
4423         }
4424         else if(nbSame==1) {
4425           // --->  2d order pyramid of 13 nodes
4426           int apex = iSameNode;
4427           int i0 = ( apex + 1 ) % nbCorners;
4428           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4429           int i0a = apex + 3;
4430           int i1a = i1 + 3;
4431           int i01 = i0 + 3;
4432           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4433                                       nextNod[i0], nextNod[i1], prevNod[apex],
4434                                       prevNod[i01], midlNod[i0],
4435                                       nextNod[i01], midlNod[i1],
4436                                       prevNod[i1a], prevNod[i0a],
4437                                       nextNod[i0a], nextNod[i1a]);
4438         }
4439         else if(nbSame==2) {
4440           // --->  2d order tetrahedron of 10 nodes
4441           int n1 = iNotSameNode;
4442           int n2 = ( n1 + 1             ) % nbCorners;
4443           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4444           int n12 = n1 + 3;
4445           int n23 = n2 + 3;
4446           int n31 = n3 + 3;
4447           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4448                                        prevNod[n12], prevNod[n23], prevNod[n31],
4449                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4450         }
4451         break;
4452       }
4453       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4454         if( nbSame == 0 ) {
4455           if ( nbDouble != 4 ) break;
4456           // --->  hexahedron with 20 nodes
4457           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4458                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4459                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4460                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4461                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4462         }
4463         else if(nbSame==1) {
4464           // ---> pyramid + pentahedron - can not be created since it is needed
4465           // additional middle node at the center of face
4466           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4467           return;
4468         }
4469         else if( nbSame == 2 ) {
4470           if ( nbDouble != 2 ) break;
4471           // --->  2d order Pentahedron with 15 nodes
4472           int n1,n2,n4,n5;
4473           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4474             // iBeforeSame is same too
4475             n1 = iBeforeSame;
4476             n2 = iOpposSame;
4477             n4 = iSameNode;
4478             n5 = iAfterSame;
4479           }
4480           else {
4481             // iAfterSame is same too
4482             n1 = iSameNode;
4483             n2 = iBeforeSame;
4484             n4 = iAfterSame;
4485             n5 = iOpposSame;
4486           }
4487           int n12 = n2 + 4;
4488           int n45 = n4 + 4;
4489           int n14 = n1 + 4;
4490           int n25 = n5 + 4;
4491           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4492                                        prevNod[n4], prevNod[n5], nextNod[n5],
4493                                        prevNod[n12], midlNod[n2], nextNod[n12],
4494                                        prevNod[n45], midlNod[n5], nextNod[n45],
4495                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4496         }
4497         break;
4498       }
4499       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4500
4501         if( nbSame == 0 && nbDouble == 9 ) {
4502           // --->  tri-quadratic hexahedron with 27 nodes
4503           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4504                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4505                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4506                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4507                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4508                                        prevNod[8], // bottom center
4509                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4510                                        nextNod[8], // top center
4511                                        midlNod[8]);// elem center
4512         }
4513         else
4514         {
4515           return;
4516         }
4517         break;
4518       }
4519       case SMDSEntity_Polygon: { // sweep POLYGON
4520
4521         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4522           // --->  hexagonal prism
4523           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4524                                        prevNod[3], prevNod[4], prevNod[5],
4525                                        nextNod[0], nextNod[1], nextNod[2],
4526                                        nextNod[3], nextNod[4], nextNod[5]);
4527         }
4528         break;
4529       }
4530       case SMDSEntity_Ball:
4531         return;
4532
4533       default:
4534         break;
4535       }
4536     }
4537
4538     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4539     {
4540       if ( baseType != SMDSEntity_Polygon )
4541       {
4542         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4543         SMDS_MeshCell::applyInterlace( ind, prevNod );
4544         SMDS_MeshCell::applyInterlace( ind, nextNod );
4545         SMDS_MeshCell::applyInterlace( ind, midlNod );
4546         SMDS_MeshCell::applyInterlace( ind, itNN );
4547         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4548         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4549       }
4550       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4551       vector<int> quantities (nbNodes + 2);
4552       polyedre_nodes.clear();
4553       quantities.clear();
4554
4555       // bottom of prism
4556       for (int inode = 0; inode < nbNodes; inode++)
4557         polyedre_nodes.push_back( prevNod[inode] );
4558       quantities.push_back( nbNodes );
4559
4560       // top of prism
4561       polyedre_nodes.push_back( nextNod[0] );
4562       for (int inode = nbNodes; inode-1; --inode )
4563         polyedre_nodes.push_back( nextNod[inode-1] );
4564       quantities.push_back( nbNodes );
4565
4566       // side faces
4567       for (int iface = 0; iface < nbNodes; iface++)
4568       {
4569         const int prevNbNodes = polyedre_nodes.size();
4570         int inextface = (iface+1) % nbNodes;
4571         polyedre_nodes.push_back( prevNod[inextface] );
4572         polyedre_nodes.push_back( prevNod[iface] );
4573         if ( prevNod[iface] != nextNod[iface] )
4574         {
4575           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4576           polyedre_nodes.push_back( nextNod[iface] );
4577         }
4578         if ( prevNod[inextface] != nextNod[inextface] )
4579         {
4580           polyedre_nodes.push_back( nextNod[inextface] );
4581           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4582         }
4583         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4584         if ( nbFaceNodes > 2 )
4585           quantities.push_back( nbFaceNodes );
4586         else // degenerated face
4587           polyedre_nodes.resize( prevNbNodes );
4588       }
4589       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4590     }
4591
4592     if ( aNewElem ) {
4593       newElems.push_back( aNewElem );
4594       myLastCreatedElems.Append(aNewElem);
4595       srcElements.Append( elem );
4596     }
4597
4598     // set new prev nodes
4599     for ( iNode = 0; iNode < nbNodes; iNode++ )
4600       prevNod[ iNode ] = nextNod[ iNode ];
4601
4602   } // for steps
4603 }
4604
4605 //=======================================================================
4606 /*!
4607  * \brief Create 1D and 2D elements around swept elements
4608  * \param mapNewNodes - source nodes and ones generated from them
4609  * \param newElemsMap - source elements and ones generated from them
4610  * \param elemNewNodesMap - nodes generated from each node of each element
4611  * \param elemSet - all swept elements
4612  * \param nbSteps - number of sweeping steps
4613  * \param srcElements - to append elem for each generated element
4614  */
4615 //=======================================================================
4616
4617 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4618                                   TTElemOfElemListMap &    newElemsMap,
4619                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4620                                   TIDSortedElemSet&        elemSet,
4621                                   const int                nbSteps,
4622                                   SMESH_SequenceOfElemPtr& srcElements)
4623 {
4624   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4625   SMESHDS_Mesh* aMesh = GetMeshDS();
4626
4627   // Find nodes belonging to only one initial element - sweep them into edges.
4628
4629   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4630   for ( ; nList != mapNewNodes.end(); nList++ )
4631   {
4632     const SMDS_MeshNode* node =
4633       static_cast<const SMDS_MeshNode*>( nList->first );
4634     if ( newElemsMap.count( node ))
4635       continue; // node was extruded into edge
4636     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4637     int nbInitElems = 0;
4638     const SMDS_MeshElement* el = 0;
4639     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4640     while ( eIt->more() && nbInitElems < 2 ) {
4641       el = eIt->next();
4642       SMDSAbs_ElementType type = el->GetType();
4643       if ( type == SMDSAbs_Volume || type < highType ) continue;
4644       if ( type > highType ) {
4645         nbInitElems = 0;
4646         highType = type;
4647       }
4648       nbInitElems += elemSet.count(el);
4649     }
4650     if ( nbInitElems < 2 ) {
4651       bool NotCreateEdge = el && el->IsMediumNode(node);
4652       if(!NotCreateEdge) {
4653         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4654         list<const SMDS_MeshElement*> newEdges;
4655         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4656       }
4657     }
4658   }
4659
4660   // Make a ceiling for each element ie an equal element of last new nodes.
4661   // Find free links of faces - make edges and sweep them into faces.
4662
4663   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4664   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4665   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4666   {
4667     const SMDS_MeshElement* elem = itElem->first;
4668     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4669
4670     if(itElem->second.size()==0) continue;
4671
4672     const bool isQuadratic = elem->IsQuadratic();
4673
4674     if ( elem->GetType() == SMDSAbs_Edge ) {
4675       // create a ceiling edge
4676       if ( !isQuadratic ) {
4677         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4678                                vecNewNodes[ 1 ]->second.back())) {
4679           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4680                                                    vecNewNodes[ 1 ]->second.back()));
4681           srcElements.Append( elem );
4682         }
4683       }
4684       else {
4685         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4686                                vecNewNodes[ 1 ]->second.back(),
4687                                vecNewNodes[ 2 ]->second.back())) {
4688           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4689                                                    vecNewNodes[ 1 ]->second.back(),
4690                                                    vecNewNodes[ 2 ]->second.back()));
4691           srcElements.Append( elem );
4692         }
4693       }
4694     }
4695     if ( elem->GetType() != SMDSAbs_Face )
4696       continue;
4697
4698     bool hasFreeLinks = false;
4699
4700     TIDSortedElemSet avoidSet;
4701     avoidSet.insert( elem );
4702
4703     set<const SMDS_MeshNode*> aFaceLastNodes;
4704     int iNode, nbNodes = vecNewNodes.size();
4705     if ( !isQuadratic ) {
4706       // loop on the face nodes
4707       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4708         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4709         // look for free links of the face
4710         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4711         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4712         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4713         // check if a link n1-n2 is free
4714         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4715           hasFreeLinks = true;
4716           // make a new edge and a ceiling for a new edge
4717           const SMDS_MeshElement* edge;
4718           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4719             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4720             srcElements.Append( myLastCreatedElems.Last() );
4721           }
4722           n1 = vecNewNodes[ iNode ]->second.back();
4723           n2 = vecNewNodes[ iNext ]->second.back();
4724           if ( !aMesh->FindEdge( n1, n2 )) {
4725             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4726             srcElements.Append( edge );
4727           }
4728         }
4729       }
4730     }
4731     else { // elem is quadratic face
4732       int nbn = nbNodes/2;
4733       for ( iNode = 0; iNode < nbn; iNode++ ) {
4734         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4735         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4736         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4737         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4738         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4739         // check if a link is free
4740         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4741              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4742              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4743           hasFreeLinks = true;
4744           // make an edge and a ceiling for a new edge
4745           // find medium node
4746           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4747             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4748             srcElements.Append( elem );
4749           }
4750           n1 = vecNewNodes[ iNode ]->second.back();
4751           n2 = vecNewNodes[ iNext ]->second.back();
4752           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4753           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4754             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4755             srcElements.Append( elem );
4756           }
4757         }
4758       }
4759       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4760         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4761       }
4762     }
4763
4764     // sweep free links into faces
4765
4766     if ( hasFreeLinks )  {
4767       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4768       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4769
4770       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4771       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4772       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4773         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4774         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4775       }
4776       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4777         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4778         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4779       }
4780       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4781         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4782         std::advance( v, volNb );
4783         // find indices of free faces of a volume and their source edges
4784         list< int > freeInd;
4785         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4786         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4787         int iF, nbF = vTool.NbFaces();
4788         for ( iF = 0; iF < nbF; iF ++ ) {
4789           if (vTool.IsFreeFace( iF ) &&
4790               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4791               initNodeSet != faceNodeSet) // except an initial face
4792           {
4793             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4794               continue;
4795             if ( faceNodeSet == initNodeSetNoCenter )
4796               continue;
4797             freeInd.push_back( iF );
4798             // find source edge of a free face iF
4799             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4800             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4801             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4802                                    initNodeSet.begin(), initNodeSet.end(),
4803                                    commonNodes.begin());
4804             if ( (*v)->IsQuadratic() )
4805               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4806             else
4807               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4808 #ifdef _DEBUG_
4809             if ( !srcEdges.back() )
4810             {
4811               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4812                    << iF << " of volume #" << vTool.ID() << endl;
4813             }
4814 #endif
4815           }
4816         }
4817         if ( freeInd.empty() )
4818           continue;
4819
4820         // create faces for all steps;
4821         // if such a face has been already created by sweep of edge,
4822         // assure that its orientation is OK
4823         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4824           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4825           vTool.SetExternalNormal();
4826           const int nextShift = vTool.IsForward() ? +1 : -1;
4827           list< int >::iterator ind = freeInd.begin();
4828           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4829           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4830           {
4831             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4832             int nbn = vTool.NbFaceNodes( *ind );
4833             const SMDS_MeshElement * f = 0;
4834             if ( nbn == 3 )              ///// triangle
4835             {
4836               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4837               if ( !f ||
4838                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4839               {
4840                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4841                                                      nodes[ 1 ],
4842                                                      nodes[ 1 + nextShift ] };
4843                 if ( f )
4844                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4845                 else
4846                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4847                                                             newOrder[ 2 ] ));
4848               }
4849             }
4850             else if ( nbn == 4 )       ///// quadrangle
4851             {
4852               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4853               if ( !f ||
4854                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4855               {
4856                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4857                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4858                 if ( f )
4859                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4860                 else
4861                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4862                                                             newOrder[ 2 ], newOrder[ 3 ]));
4863               }
4864             }
4865             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4866             {
4867               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4868               if ( !f ||
4869                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4870               {
4871                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4872                                                      nodes[2],
4873                                                      nodes[2 + 2*nextShift],
4874                                                      nodes[3 - 2*nextShift],
4875                                                      nodes[3],
4876                                                      nodes[3 + 2*nextShift]};
4877                 if ( f )
4878                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4879                 else
4880                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4881                                                             newOrder[ 1 ],
4882                                                             newOrder[ 2 ],
4883                                                             newOrder[ 3 ],
4884                                                             newOrder[ 4 ],
4885                                                             newOrder[ 5 ] ));
4886               }
4887             }
4888             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4889             {
4890               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4891                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4892               if ( !f ||
4893                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4894               {
4895                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4896                                                      nodes[4 - 2*nextShift],
4897                                                      nodes[4],
4898                                                      nodes[4 + 2*nextShift],
4899                                                      nodes[1],
4900                                                      nodes[5 - 2*nextShift],
4901                                                      nodes[5],
4902                                                      nodes[5 + 2*nextShift] };
4903                 if ( f )
4904                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4905                 else
4906                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4907                                                            newOrder[ 2 ], newOrder[ 3 ],
4908                                                            newOrder[ 4 ], newOrder[ 5 ],
4909                                                            newOrder[ 6 ], newOrder[ 7 ]));
4910               }
4911             }
4912             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4913             {
4914               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4915                                       SMDSAbs_Face, /*noMedium=*/false);
4916               if ( !f ||
4917                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4918               {
4919                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4920                                                      nodes[4 - 2*nextShift],
4921                                                      nodes[4],
4922                                                      nodes[4 + 2*nextShift],
4923                                                      nodes[1],
4924                                                      nodes[5 - 2*nextShift],
4925                                                      nodes[5],
4926                                                      nodes[5 + 2*nextShift],
4927                                                      nodes[8] };
4928                 if ( f )
4929                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4930                 else
4931                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4932                                                            newOrder[ 2 ], newOrder[ 3 ],
4933                                                            newOrder[ 4 ], newOrder[ 5 ],
4934                                                            newOrder[ 6 ], newOrder[ 7 ],
4935                                                            newOrder[ 8 ]));
4936               }
4937             }
4938             else  //////// polygon
4939             {
4940               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4941               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4942               if ( !f ||
4943                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4944               {
4945                 if ( !vTool.IsForward() )
4946                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4947                 if ( f )
4948                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4949                 else
4950                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4951               }
4952             }
4953
4954             while ( srcElements.Length() < myLastCreatedElems.Length() )
4955               srcElements.Append( *srcEdge );
4956
4957           }  // loop on free faces
4958
4959           // go to the next volume
4960           iVol = 0;
4961           while ( iVol++ < nbVolumesByStep ) v++;
4962
4963         } // loop on steps
4964       } // loop on volumes of one step
4965     } // sweep free links into faces
4966
4967     // Make a ceiling face with a normal external to a volume
4968
4969     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4970     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4971     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4972
4973     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4974       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4975       iF = lastVol.GetFaceIndex( aFaceLastNodes );
4976     }
4977     if ( iF >= 0 ) {
4978       lastVol.SetExternalNormal();
4979       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4980       int nbn = lastVol.NbFaceNodes( iF );
4981       // we do not use this->AddElement() because nodes are interlaced
4982       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4983       if ( !hasFreeLinks ||
4984            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4985       {
4986         if ( nbn == 3 )
4987           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4988
4989         else if ( nbn == 4 )
4990           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4991
4992         else if ( nbn == 6 && isQuadratic )
4993           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4994                                                     nodes[1], nodes[3], nodes[5]));
4995         else if ( nbn == 7 && isQuadratic )
4996           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4997                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
4998         else if ( nbn == 8 && isQuadratic )
4999           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5000                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5001         else if ( nbn == 9 && isQuadratic )
5002           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5003                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5004                                                     nodes[8]));
5005         else
5006           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5007
5008         while ( srcElements.Length() < myLastCreatedElems.Length() )
5009           srcElements.Append( elem );
5010       }
5011     }
5012   } // loop on swept elements
5013 }
5014
5015 //=======================================================================
5016 //function : RotationSweep
5017 //purpose  :
5018 //=======================================================================
5019
5020 SMESH_MeshEditor::PGroupIDs
5021 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5022                                 const gp_Ax1&      theAxis,
5023                                 const double       theAngle,
5024                                 const int          theNbSteps,
5025                                 const double       theTol,
5026                                 const bool         theMakeGroups,
5027                                 const bool         theMakeWalls)
5028 {
5029   myLastCreatedElems.Clear();
5030   myLastCreatedNodes.Clear();
5031
5032   // source elements for each generated one
5033   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5034
5035   MESSAGE( "RotationSweep()");
5036   gp_Trsf aTrsf;
5037   aTrsf.SetRotation( theAxis, theAngle );
5038   gp_Trsf aTrsf2;
5039   aTrsf2.SetRotation( theAxis, theAngle/2. );
5040
5041   gp_Lin aLine( theAxis );
5042   double aSqTol = theTol * theTol;
5043
5044   SMESHDS_Mesh* aMesh = GetMeshDS();
5045
5046   TNodeOfNodeListMap mapNewNodes;
5047   TElemOfVecOfNnlmiMap mapElemNewNodes;
5048   TTElemOfElemListMap newElemsMap;
5049
5050   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5051                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5052                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5053   // loop on theElems
5054   TIDSortedElemSet::iterator itElem;
5055   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5056     const SMDS_MeshElement* elem = *itElem;
5057     if ( !elem || elem->GetType() == SMDSAbs_Volume )
5058       continue;
5059     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5060     newNodesItVec.reserve( elem->NbNodes() );
5061
5062     // loop on elem nodes
5063     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5064     while ( itN->more() )
5065     {
5066       // check if a node has been already sweeped
5067       const SMDS_MeshNode* node = cast2Node( itN->next() );
5068
5069       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5070       double coord[3];
5071       aXYZ.Coord( coord[0], coord[1], coord[2] );
5072       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5073
5074       TNodeOfNodeListMapItr nIt =
5075         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5076       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5077       if ( listNewNodes.empty() )
5078       {
5079         // check if we are to create medium nodes between corner ones
5080         bool needMediumNodes = false;
5081         if ( isQuadraticMesh )
5082         {
5083           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5084           while (it->more() && !needMediumNodes )
5085           {
5086             const SMDS_MeshElement* invElem = it->next();
5087             if ( invElem != elem && !theElems.count( invElem )) continue;
5088             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5089             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5090               needMediumNodes = true;
5091           }
5092         }
5093
5094         // make new nodes
5095         const SMDS_MeshNode * newNode = node;
5096         for ( int i = 0; i < theNbSteps; i++ ) {
5097           if ( !isOnAxis ) {
5098             if ( needMediumNodes )  // create a medium node
5099             {
5100               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5101               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5102               myLastCreatedNodes.Append(newNode);
5103               srcNodes.Append( node );
5104               listNewNodes.push_back( newNode );
5105               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5106             }
5107             else {
5108               aTrsf.Transforms( coord[0], coord[1], coord[2] );
5109             }
5110             // create a corner node
5111             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5112             myLastCreatedNodes.Append(newNode);
5113             srcNodes.Append( node );
5114             listNewNodes.push_back( newNode );
5115           }
5116           else {
5117             listNewNodes.push_back( newNode );
5118             // if ( needMediumNodes )
5119             //   listNewNodes.push_back( newNode );
5120           }
5121         }
5122       }
5123       newNodesItVec.push_back( nIt );
5124     }
5125     // make new elements
5126     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5127   }
5128
5129   if ( theMakeWalls )
5130     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5131
5132   PGroupIDs newGroupIDs;
5133   if ( theMakeGroups )
5134     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5135
5136   return newGroupIDs;
5137 }
5138
5139
5140 //=======================================================================
5141 //function : CreateNode
5142 //purpose  :
5143 //=======================================================================
5144 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5145                                                   const double y,
5146                                                   const double z,
5147                                                   const double tolnode,
5148                                                   SMESH_SequenceOfNode& aNodes)
5149 {
5150   // myLastCreatedElems.Clear();
5151   // myLastCreatedNodes.Clear();
5152
5153   gp_Pnt P1(x,y,z);
5154   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5155
5156   // try to search in sequence of existing nodes
5157   // if aNodes.Length()>0 we 'nave to use given sequence
5158   // else - use all nodes of mesh
5159   if(aNodes.Length()>0) {
5160     int i;
5161     for(i=1; i<=aNodes.Length(); i++) {
5162       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5163       if(P1.Distance(P2)<tolnode)
5164         return aNodes.Value(i);
5165     }
5166   }
5167   else {
5168     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5169     while(itn->more()) {
5170       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5171       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5172       if(P1.Distance(P2)<tolnode)
5173         return aN;
5174     }
5175   }
5176
5177   // create new node and return it
5178   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5179   //myLastCreatedNodes.Append(NewNode);
5180   return NewNode;
5181 }
5182
5183
5184 //=======================================================================
5185 //function : ExtrusionSweep
5186 //purpose  :
5187 //=======================================================================
5188
5189 SMESH_MeshEditor::PGroupIDs
5190 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5191                                   const gp_Vec&        theStep,
5192                                   const int            theNbSteps,
5193                                   TTElemOfElemListMap& newElemsMap,
5194                                   const bool           theMakeGroups,
5195                                   const int            theFlags,
5196                                   const double         theTolerance)
5197 {
5198   ExtrusParam aParams;
5199   aParams.myDir = gp_Dir(theStep);
5200   aParams.myNodes.Clear();
5201   aParams.mySteps = new TColStd_HSequenceOfReal;
5202   int i;
5203   for(i=1; i<=theNbSteps; i++)
5204     aParams.mySteps->Append(theStep.Magnitude());
5205
5206   return
5207     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5208 }
5209
5210
5211 //=======================================================================
5212 //function : ExtrusionSweep
5213 //purpose  :
5214 //=======================================================================
5215
5216 SMESH_MeshEditor::PGroupIDs
5217 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5218                                   ExtrusParam&         theParams,
5219                                   TTElemOfElemListMap& newElemsMap,
5220                                   const bool           theMakeGroups,
5221                                   const int            theFlags,
5222                                   const double         theTolerance)
5223 {
5224   myLastCreatedElems.Clear();
5225   myLastCreatedNodes.Clear();
5226
5227   // source elements for each generated one
5228   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5229
5230   SMESHDS_Mesh* aMesh = GetMeshDS();
5231
5232   int nbsteps = theParams.mySteps->Length();
5233
5234   TNodeOfNodeListMap mapNewNodes;
5235   //TNodeOfNodeVecMap mapNewNodes;
5236   TElemOfVecOfNnlmiMap mapElemNewNodes;
5237   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5238
5239   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5240                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5241                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5242   // loop on theElems
5243   TIDSortedElemSet::iterator itElem;
5244   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5245     // check element type
5246     const SMDS_MeshElement* elem = *itElem;
5247     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5248       continue;
5249
5250     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5251     newNodesItVec.reserve( elem->NbNodes() );
5252
5253     // loop on elem nodes
5254     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5255     while ( itN->more() )
5256     {
5257       // check if a node has been already sweeped
5258       const SMDS_MeshNode* node = cast2Node( itN->next() );
5259       TNodeOfNodeListMap::iterator nIt =
5260         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5261       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5262       if ( listNewNodes.empty() )
5263       {
5264         // make new nodes
5265
5266         // check if we are to create medium nodes between corner ones
5267         bool needMediumNodes = false;
5268         if ( isQuadraticMesh )
5269         {
5270           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5271           while (it->more() && !needMediumNodes )
5272           {
5273             const SMDS_MeshElement* invElem = it->next();
5274             if ( invElem != elem && !theElems.count( invElem )) continue;
5275             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5276             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5277               needMediumNodes = true;
5278           }
5279         }
5280
5281         double coord[] = { node->X(), node->Y(), node->Z() };
5282         for ( int i = 0; i < nbsteps; i++ )
5283         {
5284           if ( needMediumNodes ) // create a medium node
5285           {
5286             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5287             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5288             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5289             if( theFlags & EXTRUSION_FLAG_SEW ) {
5290               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5291                                                          theTolerance, theParams.myNodes);
5292               listNewNodes.push_back( newNode );
5293             }
5294             else {
5295               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5296               myLastCreatedNodes.Append(newNode);
5297               srcNodes.Append( node );
5298               listNewNodes.push_back( newNode );
5299             }
5300           }
5301           // create a corner node
5302           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5303           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5304           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5305           if( theFlags & EXTRUSION_FLAG_SEW ) {
5306             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5307                                                        theTolerance, theParams.myNodes);
5308             listNewNodes.push_back( newNode );
5309           }
5310           else {
5311             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5312             myLastCreatedNodes.Append(newNode);
5313             srcNodes.Append( node );
5314             listNewNodes.push_back( newNode );
5315           }
5316         }
5317       }
5318       newNodesItVec.push_back( nIt );
5319     }
5320     // make new elements
5321     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5322   }
5323
5324   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5325     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5326   }
5327   PGroupIDs newGroupIDs;
5328   if ( theMakeGroups )
5329     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5330
5331   return newGroupIDs;
5332 }
5333
5334 //=======================================================================
5335 //function : ExtrusionAlongTrack
5336 //purpose  :
5337 //=======================================================================
5338 SMESH_MeshEditor::Extrusion_Error
5339 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5340                                        SMESH_subMesh*       theTrack,
5341                                        const SMDS_MeshNode* theN1,
5342                                        const bool           theHasAngles,
5343                                        list<double>&        theAngles,
5344                                        const bool           theLinearVariation,
5345                                        const bool           theHasRefPoint,
5346                                        const gp_Pnt&        theRefPoint,
5347                                        const bool           theMakeGroups)
5348 {
5349   MESSAGE("ExtrusionAlongTrack");
5350   myLastCreatedElems.Clear();
5351   myLastCreatedNodes.Clear();
5352
5353   int aNbE;
5354   std::list<double> aPrms;
5355   TIDSortedElemSet::iterator itElem;
5356
5357   gp_XYZ aGC;
5358   TopoDS_Edge aTrackEdge;
5359   TopoDS_Vertex aV1, aV2;
5360
5361   SMDS_ElemIteratorPtr aItE;
5362   SMDS_NodeIteratorPtr aItN;
5363   SMDSAbs_ElementType aTypeE;
5364
5365   TNodeOfNodeListMap mapNewNodes;
5366
5367   // 1. Check data
5368   aNbE = theElements.size();
5369   // nothing to do
5370   if ( !aNbE )
5371     return EXTR_NO_ELEMENTS;
5372
5373   // 1.1 Track Pattern
5374   ASSERT( theTrack );
5375
5376   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5377
5378   aItE = pSubMeshDS->GetElements();
5379   while ( aItE->more() ) {
5380     const SMDS_MeshElement* pE = aItE->next();
5381     aTypeE = pE->GetType();
5382     // Pattern must contain links only
5383     if ( aTypeE != SMDSAbs_Edge )
5384       return EXTR_PATH_NOT_EDGE;
5385   }
5386
5387   list<SMESH_MeshEditor_PathPoint> fullList;
5388
5389   const TopoDS_Shape& aS = theTrack->GetSubShape();
5390   // Sub-shape for the Pattern must be an Edge or Wire
5391   if( aS.ShapeType() == TopAbs_EDGE ) {
5392     aTrackEdge = TopoDS::Edge( aS );
5393     // the Edge must not be degenerated
5394     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5395       return EXTR_BAD_PATH_SHAPE;
5396     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5397     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5398     const SMDS_MeshNode* aN1 = aItN->next();
5399     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5400     const SMDS_MeshNode* aN2 = aItN->next();
5401     // starting node must be aN1 or aN2
5402     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5403       return EXTR_BAD_STARTING_NODE;
5404     aItN = pSubMeshDS->GetNodes();
5405     while ( aItN->more() ) {
5406       const SMDS_MeshNode* pNode = aItN->next();
5407       const SMDS_EdgePosition* pEPos =
5408         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5409       double aT = pEPos->GetUParameter();
5410       aPrms.push_back( aT );
5411     }
5412     //Extrusion_Error err =
5413     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5414   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5415     list< SMESH_subMesh* > LSM;
5416     TopTools_SequenceOfShape Edges;
5417     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5418     while(itSM->more()) {
5419       SMESH_subMesh* SM = itSM->next();
5420       LSM.push_back(SM);
5421       const TopoDS_Shape& aS = SM->GetSubShape();
5422       Edges.Append(aS);
5423     }
5424     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5425     int startNid = theN1->GetID();
5426     TColStd_MapOfInteger UsedNums;
5427
5428     int NbEdges = Edges.Length();
5429     int i = 1;
5430     for(; i<=NbEdges; i++) {
5431       int k = 0;
5432       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5433       for(; itLSM!=LSM.end(); itLSM++) {
5434         k++;
5435         if(UsedNums.Contains(k)) continue;
5436         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5437         SMESH_subMesh* locTrack = *itLSM;
5438         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5439         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5440         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5441         const SMDS_MeshNode* aN1 = aItN->next();
5442         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5443         const SMDS_MeshNode* aN2 = aItN->next();
5444         // starting node must be aN1 or aN2
5445         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5446         // 2. Collect parameters on the track edge
5447         aPrms.clear();
5448         aItN = locMeshDS->GetNodes();
5449         while ( aItN->more() ) {
5450           const SMDS_MeshNode* pNode = aItN->next();
5451           const SMDS_EdgePosition* pEPos =
5452             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5453           double aT = pEPos->GetUParameter();
5454           aPrms.push_back( aT );
5455         }
5456         list<SMESH_MeshEditor_PathPoint> LPP;
5457         //Extrusion_Error err =
5458         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5459         LLPPs.push_back(LPP);
5460         UsedNums.Add(k);
5461         // update startN for search following egde
5462         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5463         else startNid = aN1->GetID();
5464         break;
5465       }
5466     }
5467     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5468     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5469     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5470     for(; itPP!=firstList.end(); itPP++) {
5471       fullList.push_back( *itPP );
5472     }
5473     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5474     fullList.pop_back();
5475     itLLPP++;
5476     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5477       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5478       itPP = currList.begin();
5479       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5480       gp_Dir D1 = PP1.Tangent();
5481       gp_Dir D2 = PP2.Tangent();
5482       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5483                            (D1.Z()+D2.Z())/2 ) );
5484       PP1.SetTangent(Dnew);
5485       fullList.push_back(PP1);
5486       itPP++;
5487       for(; itPP!=firstList.end(); itPP++) {
5488         fullList.push_back( *itPP );
5489       }
5490       PP1 = fullList.back();
5491       fullList.pop_back();
5492     }
5493     // if wire not closed
5494     fullList.push_back(PP1);
5495     // else ???
5496   }
5497   else {
5498     return EXTR_BAD_PATH_SHAPE;
5499   }
5500
5501   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5502                           theHasRefPoint, theRefPoint, theMakeGroups);
5503 }
5504
5505
5506 //=======================================================================
5507 //function : ExtrusionAlongTrack
5508 //purpose  :
5509 //=======================================================================
5510 SMESH_MeshEditor::Extrusion_Error
5511 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5512                                        SMESH_Mesh*          theTrack,
5513                                        const SMDS_MeshNode* theN1,
5514                                        const bool           theHasAngles,
5515                                        list<double>&        theAngles,
5516                                        const bool           theLinearVariation,
5517                                        const bool           theHasRefPoint,
5518                                        const gp_Pnt&        theRefPoint,
5519                                        const bool           theMakeGroups)
5520 {
5521   myLastCreatedElems.Clear();
5522   myLastCreatedNodes.Clear();
5523
5524   int aNbE;
5525   std::list<double> aPrms;
5526   TIDSortedElemSet::iterator itElem;
5527
5528   gp_XYZ aGC;
5529   TopoDS_Edge aTrackEdge;
5530   TopoDS_Vertex aV1, aV2;
5531
5532   SMDS_ElemIteratorPtr aItE;
5533   SMDS_NodeIteratorPtr aItN;
5534   SMDSAbs_ElementType aTypeE;
5535
5536   TNodeOfNodeListMap mapNewNodes;
5537
5538   // 1. Check data
5539   aNbE = theElements.size();
5540   // nothing to do
5541   if ( !aNbE )
5542     return EXTR_NO_ELEMENTS;
5543
5544   // 1.1 Track Pattern
5545   ASSERT( theTrack );
5546
5547   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5548
5549   aItE = pMeshDS->elementsIterator();
5550   while ( aItE->more() ) {
5551     const SMDS_MeshElement* pE = aItE->next();
5552     aTypeE = pE->GetType();
5553     // Pattern must contain links only
5554     if ( aTypeE != SMDSAbs_Edge )
5555       return EXTR_PATH_NOT_EDGE;
5556   }
5557
5558   list<SMESH_MeshEditor_PathPoint> fullList;
5559
5560   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5561
5562   if ( !theTrack->HasShapeToMesh() ) {
5563     //Mesh without shape
5564     const SMDS_MeshNode* currentNode = NULL;
5565     const SMDS_MeshNode* prevNode = theN1;
5566     std::vector<const SMDS_MeshNode*> aNodesList;
5567     aNodesList.push_back(theN1);
5568     int nbEdges = 0, conn=0;
5569     const SMDS_MeshElement* prevElem = NULL;
5570     const SMDS_MeshElement* currentElem = NULL;
5571     int totalNbEdges = theTrack->NbEdges();
5572     SMDS_ElemIteratorPtr nIt;
5573
5574     //check start node
5575     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5576       return EXTR_BAD_STARTING_NODE;
5577     }
5578
5579     conn = nbEdgeConnectivity(theN1);
5580     if(conn > 2)
5581       return EXTR_PATH_NOT_EDGE;
5582
5583     aItE = theN1->GetInverseElementIterator();
5584     prevElem = aItE->next();
5585     currentElem = prevElem;
5586     //Get all nodes
5587     if(totalNbEdges == 1 ) {
5588       nIt = currentElem->nodesIterator();
5589       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5590       if(currentNode == prevNode)
5591         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5592       aNodesList.push_back(currentNode);
5593     } else {
5594       nIt = currentElem->nodesIterator();
5595       while( nIt->more() ) {
5596         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5597         if(currentNode == prevNode)
5598           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5599         aNodesList.push_back(currentNode);
5600
5601         //case of the closed mesh
5602         if(currentNode == theN1) {
5603           nbEdges++;
5604           break;
5605         }
5606
5607         conn = nbEdgeConnectivity(currentNode);
5608         if(conn > 2) {
5609           return EXTR_PATH_NOT_EDGE;
5610         }else if( conn == 1 && nbEdges > 0 ) {
5611           //End of the path
5612           nbEdges++;
5613           break;
5614         }else {
5615           prevNode = currentNode;
5616           aItE = currentNode->GetInverseElementIterator();
5617           currentElem = aItE->next();
5618           if( currentElem  == prevElem)
5619             currentElem = aItE->next();
5620           nIt = currentElem->nodesIterator();
5621           prevElem = currentElem;
5622           nbEdges++;
5623         }
5624       }
5625     }
5626
5627     if(nbEdges != totalNbEdges)
5628       return EXTR_PATH_NOT_EDGE;
5629
5630     TopTools_SequenceOfShape Edges;
5631     double x1,x2,y1,y2,z1,z2;
5632     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5633     int startNid = theN1->GetID();
5634     for(int i = 1; i < aNodesList.size(); i++) {
5635       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5636       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5637       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5638       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5639       list<SMESH_MeshEditor_PathPoint> LPP;
5640       aPrms.clear();
5641       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5642       LLPPs.push_back(LPP);
5643       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5644       else startNid = aNodesList[i-1]->GetID();
5645
5646     }
5647
5648     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5649     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5650     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5651     for(; itPP!=firstList.end(); itPP++) {
5652       fullList.push_back( *itPP );
5653     }
5654
5655     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5656     SMESH_MeshEditor_PathPoint PP2;
5657     fullList.pop_back();
5658     itLLPP++;
5659     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5660       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5661       itPP = currList.begin();
5662       PP2 = currList.front();
5663       gp_Dir D1 = PP1.Tangent();
5664       gp_Dir D2 = PP2.Tangent();
5665       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5666                            (D1.Z()+D2.Z())/2 ) );
5667       PP1.SetTangent(Dnew);
5668       fullList.push_back(PP1);
5669       itPP++;
5670       for(; itPP!=currList.end(); itPP++) {
5671         fullList.push_back( *itPP );
5672       }
5673       PP1 = fullList.back();
5674       fullList.pop_back();
5675     }
5676     fullList.push_back(PP1);
5677
5678   } // Sub-shape for the Pattern must be an Edge or Wire
5679   else if( aS.ShapeType() == TopAbs_EDGE ) {
5680     aTrackEdge = TopoDS::Edge( aS );
5681     // the Edge must not be degenerated
5682     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5683       return EXTR_BAD_PATH_SHAPE;
5684     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5685     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5686     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5687     // starting node must be aN1 or aN2
5688     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5689       return EXTR_BAD_STARTING_NODE;
5690     aItN = pMeshDS->nodesIterator();
5691     while ( aItN->more() ) {
5692       const SMDS_MeshNode* pNode = aItN->next();
5693       if( pNode==aN1 || pNode==aN2 ) continue;
5694       const SMDS_EdgePosition* pEPos =
5695         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5696       double aT = pEPos->GetUParameter();
5697       aPrms.push_back( aT );
5698     }
5699     //Extrusion_Error err =
5700     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5701   }
5702   else if( aS.ShapeType() == TopAbs_WIRE ) {
5703     list< SMESH_subMesh* > LSM;
5704     TopTools_SequenceOfShape Edges;
5705     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5706     for(; eExp.More(); eExp.Next()) {
5707       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5708       if( SMESH_Algo::isDegenerated(E) ) continue;
5709       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5710       if(SM) {
5711         LSM.push_back(SM);
5712         Edges.Append(E);
5713       }
5714     }
5715     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5716     TopoDS_Vertex aVprev;
5717     TColStd_MapOfInteger UsedNums;
5718     int NbEdges = Edges.Length();
5719     int i = 1;
5720     for(; i<=NbEdges; i++) {
5721       int k = 0;
5722       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5723       for(; itLSM!=LSM.end(); itLSM++) {
5724         k++;
5725         if(UsedNums.Contains(k)) continue;
5726         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5727         SMESH_subMesh* locTrack = *itLSM;
5728         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5729         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5730         bool aN1isOK = false, aN2isOK = false;
5731         if ( aVprev.IsNull() ) {
5732           // if previous vertex is not yet defined, it means that we in the beginning of wire
5733           // and we have to find initial vertex corresponding to starting node theN1
5734           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5735           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5736           // starting node must be aN1 or aN2
5737           aN1isOK = ( aN1 && aN1 == theN1 );
5738           aN2isOK = ( aN2 && aN2 == theN1 );
5739         }
5740         else {
5741           // we have specified ending vertex of the previous edge on the previous iteration
5742           // and we have just to check that it corresponds to any vertex in current segment
5743           aN1isOK = aVprev.IsSame( aV1 );
5744           aN2isOK = aVprev.IsSame( aV2 );
5745         }
5746         if ( !aN1isOK && !aN2isOK ) continue;
5747         // 2. Collect parameters on the track edge
5748         aPrms.clear();
5749         aItN = locMeshDS->GetNodes();
5750         while ( aItN->more() ) {
5751           const SMDS_MeshNode*     pNode = aItN->next();
5752           const SMDS_EdgePosition* pEPos =
5753             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5754           double aT = pEPos->GetUParameter();
5755           aPrms.push_back( aT );
5756         }
5757         list<SMESH_MeshEditor_PathPoint> LPP;
5758         //Extrusion_Error err =
5759         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5760         LLPPs.push_back(LPP);
5761         UsedNums.Add(k);
5762         // update startN for search following egde
5763         if ( aN1isOK ) aVprev = aV2;
5764         else           aVprev = aV1;
5765         break;
5766       }
5767     }
5768     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5769     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5770     fullList.splice( fullList.end(), firstList );
5771
5772     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5773     fullList.pop_back();
5774     itLLPP++;
5775     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5776       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5777       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5778       gp_Dir D1 = PP1.Tangent();
5779       gp_Dir D2 = PP2.Tangent();
5780       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5781       PP1.SetTangent(Dnew);
5782       fullList.push_back(PP1);
5783       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5784       PP1 = fullList.back();
5785       fullList.pop_back();
5786     }
5787     // if wire not closed
5788     fullList.push_back(PP1);
5789     // else ???
5790   }
5791   else {
5792     return EXTR_BAD_PATH_SHAPE;
5793   }
5794
5795   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5796                           theHasRefPoint, theRefPoint, theMakeGroups);
5797 }
5798
5799
5800 //=======================================================================
5801 //function : MakeEdgePathPoints
5802 //purpose  : auxilary for ExtrusionAlongTrack
5803 //=======================================================================
5804 SMESH_MeshEditor::Extrusion_Error
5805 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
5806                                      const TopoDS_Edge&                aTrackEdge,
5807                                      bool                              FirstIsStart,
5808                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5809 {
5810   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5811   aTolVec=1.e-7;
5812   aTolVec2=aTolVec*aTolVec;
5813   double aT1, aT2;
5814   TopoDS_Vertex aV1, aV2;
5815   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5816   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5817   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5818   // 2. Collect parameters on the track edge
5819   aPrms.push_front( aT1 );
5820   aPrms.push_back( aT2 );
5821   // sort parameters
5822   aPrms.sort();
5823   if( FirstIsStart ) {
5824     if ( aT1 > aT2 ) {
5825       aPrms.reverse();
5826     }
5827   }
5828   else {
5829     if ( aT2 > aT1 ) {
5830       aPrms.reverse();
5831     }
5832   }
5833   // 3. Path Points
5834   SMESH_MeshEditor_PathPoint aPP;
5835   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5836   std::list<double>::iterator aItD = aPrms.begin();
5837   for(; aItD != aPrms.end(); ++aItD) {
5838     double aT = *aItD;
5839     gp_Pnt aP3D;
5840     gp_Vec aVec;
5841     aC3D->D1( aT, aP3D, aVec );
5842     aL2 = aVec.SquareMagnitude();
5843     if ( aL2 < aTolVec2 )
5844       return EXTR_CANT_GET_TANGENT;
5845     gp_Dir aTgt( aVec );
5846     aPP.SetPnt( aP3D );
5847     aPP.SetTangent( aTgt );
5848     aPP.SetParameter( aT );
5849     LPP.push_back(aPP);
5850   }
5851   return EXTR_OK;
5852 }
5853
5854
5855 //=======================================================================
5856 //function : MakeExtrElements
5857 //purpose  : auxilary for ExtrusionAlongTrack
5858 //=======================================================================
5859 SMESH_MeshEditor::Extrusion_Error
5860 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&                 theElements,
5861                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5862                                    const bool                        theHasAngles,
5863                                    list<double>&                     theAngles,
5864                                    const bool                        theLinearVariation,
5865                                    const bool                        theHasRefPoint,
5866                                    const gp_Pnt&                     theRefPoint,
5867                                    const bool                        theMakeGroups)
5868 {
5869   const int aNbTP = fullList.size();
5870   // Angles
5871   if( theHasAngles && !theAngles.empty() && theLinearVariation )
5872     LinearAngleVariation(aNbTP-1, theAngles);
5873   // fill vector of path points with angles
5874   vector<SMESH_MeshEditor_PathPoint> aPPs;
5875   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5876   list<double>::iterator                 itAngles = theAngles.begin();
5877   aPPs.push_back( *itPP++ );
5878   for( ; itPP != fullList.end(); itPP++) {
5879     aPPs.push_back( *itPP );
5880     if ( theHasAngles && itAngles != theAngles.end() )
5881       aPPs.back().SetAngle( *itAngles++ );
5882   }
5883
5884   TNodeOfNodeListMap   mapNewNodes;
5885   TElemOfVecOfNnlmiMap mapElemNewNodes;
5886   TTElemOfElemListMap  newElemsMap;
5887   TIDSortedElemSet::iterator itElem;
5888   // source elements for each generated one
5889   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5890
5891   // 3. Center of rotation aV0
5892   gp_Pnt aV0 = theRefPoint;
5893   if ( !theHasRefPoint )
5894   {
5895     gp_XYZ aGC( 0.,0.,0. );
5896     TIDSortedElemSet newNodes;
5897
5898     itElem = theElements.begin();
5899     for ( ; itElem != theElements.end(); itElem++ ) {
5900       const SMDS_MeshElement* elem = *itElem;
5901
5902       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5903       while ( itN->more() ) {
5904         const SMDS_MeshElement* node = itN->next();
5905         if ( newNodes.insert( node ).second )
5906           aGC += SMESH_TNodeXYZ( node );
5907       }
5908     }
5909     aGC /= newNodes.size();
5910     aV0.SetXYZ( aGC );
5911   } // if (!theHasRefPoint) {
5912
5913   // 4. Processing the elements
5914   SMESHDS_Mesh* aMesh = GetMeshDS();
5915
5916   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5917     // check element type
5918     const SMDS_MeshElement* elem = *itElem;
5919     SMDSAbs_ElementType   aTypeE = elem->GetType();
5920     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5921       continue;
5922
5923     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5924     newNodesItVec.reserve( elem->NbNodes() );
5925
5926     // loop on elem nodes
5927     int nodeIndex = -1;
5928     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5929     while ( itN->more() )
5930     {
5931       ++nodeIndex;
5932       // check if a node has been already processed
5933       const SMDS_MeshNode* node =
5934         static_cast<const SMDS_MeshNode*>( itN->next() );
5935       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5936       if ( nIt == mapNewNodes.end() ) {
5937         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5938         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5939
5940         // make new nodes
5941         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5942         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5943         gp_Ax1 anAx1, anAxT1T0;
5944         gp_Dir aDT1x, aDT0x, aDT1T0;
5945
5946         aTolAng=1.e-4;
5947
5948         aV0x = aV0;
5949         aPN0 = SMESH_TNodeXYZ( node );
5950
5951         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5952         aP0x = aPP0.Pnt();
5953         aDT0x= aPP0.Tangent();
5954         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5955
5956         for ( int j = 1; j < aNbTP; ++j ) {
5957           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5958           aP1x     = aPP1.Pnt();
5959           aDT1x    = aPP1.Tangent();
5960           aAngle1x = aPP1.Angle();
5961
5962           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5963           // Translation
5964           gp_Vec aV01x( aP0x, aP1x );
5965           aTrsf.SetTranslation( aV01x );
5966
5967           // traslated point
5968           aV1x = aV0x.Transformed( aTrsf );
5969           aPN1 = aPN0.Transformed( aTrsf );
5970
5971           // rotation 1 [ T1,T0 ]
5972           aAngleT1T0=-aDT1x.Angle( aDT0x );
5973           if (fabs(aAngleT1T0) > aTolAng) {
5974             aDT1T0=aDT1x^aDT0x;
5975             anAxT1T0.SetLocation( aV1x );
5976             anAxT1T0.SetDirection( aDT1T0 );
5977             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5978
5979             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5980           }
5981
5982           // rotation 2
5983           if ( theHasAngles ) {
5984             anAx1.SetLocation( aV1x );
5985             anAx1.SetDirection( aDT1x );
5986             aTrsfRot.SetRotation( anAx1, aAngle1x );
5987
5988             aPN1 = aPN1.Transformed( aTrsfRot );
5989           }
5990
5991           // make new node
5992           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5993           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5994             // create additional node
5995             double x = ( aPN1.X() + aPN0.X() )/2.;
5996             double y = ( aPN1.Y() + aPN0.Y() )/2.;
5997             double z = ( aPN1.Z() + aPN0.Z() )/2.;
5998             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5999             myLastCreatedNodes.Append(newNode);
6000             srcNodes.Append( node );
6001             listNewNodes.push_back( newNode );
6002           }
6003           const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6004           myLastCreatedNodes.Append(newNode);
6005           srcNodes.Append( node );
6006           listNewNodes.push_back( newNode );
6007
6008           aPN0 = aPN1;
6009           aP0x = aP1x;
6010           aV0x = aV1x;
6011           aDT0x = aDT1x;
6012         }
6013       }
6014
6015       else {
6016         // if current elem is quadratic and current node is not medium
6017         // we have to check - may be it is needed to insert additional nodes
6018         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6019           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6020           if(listNewNodes.size()==aNbTP-1) {
6021             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6022             gp_XYZ P(node->X(), node->Y(), node->Z());
6023             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6024             int i;
6025             for(i=0; i<aNbTP-1; i++) {
6026               const SMDS_MeshNode* N = *it;
6027               double x = ( N->X() + P.X() )/2.;
6028               double y = ( N->Y() + P.Y() )/2.;
6029               double z = ( N->Z() + P.Z() )/2.;
6030               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6031               srcNodes.Append( node );
6032               myLastCreatedNodes.Append(newN);
6033               aNodes[2*i] = newN;
6034               aNodes[2*i+1] = N;
6035               P = gp_XYZ(N->X(),N->Y(),N->Z());
6036             }
6037             listNewNodes.clear();
6038             for(i=0; i<2*(aNbTP-1); i++) {
6039               listNewNodes.push_back(aNodes[i]);
6040             }
6041           }
6042         }
6043       }
6044
6045       newNodesItVec.push_back( nIt );
6046     }
6047     // make new elements
6048     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6049     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6050     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6051   }
6052
6053   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6054
6055   if ( theMakeGroups )
6056     generateGroups( srcNodes, srcElems, "extruded");
6057
6058   return EXTR_OK;
6059 }
6060
6061
6062 //=======================================================================
6063 //function : LinearAngleVariation
6064 //purpose  : auxilary for ExtrusionAlongTrack
6065 //=======================================================================
6066 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6067                                             list<double>& Angles)
6068 {
6069   int nbAngles = Angles.size();
6070   if( nbSteps > nbAngles ) {
6071     vector<double> theAngles(nbAngles);
6072     list<double>::iterator it = Angles.begin();
6073     int i = -1;
6074     for(; it!=Angles.end(); it++) {
6075       i++;
6076       theAngles[i] = (*it);
6077     }
6078     list<double> res;
6079     double rAn2St = double( nbAngles ) / double( nbSteps );
6080     double angPrev = 0, angle;
6081     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6082       double angCur = rAn2St * ( iSt+1 );
6083       double angCurFloor  = floor( angCur );
6084       double angPrevFloor = floor( angPrev );
6085       if ( angPrevFloor == angCurFloor )
6086         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6087       else {
6088         int iP = int( angPrevFloor );
6089         double angPrevCeil = ceil(angPrev);
6090         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6091
6092         int iC = int( angCurFloor );
6093         if ( iC < nbAngles )
6094           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6095
6096         iP = int( angPrevCeil );
6097         while ( iC-- > iP )
6098           angle += theAngles[ iC ];
6099       }
6100       res.push_back(angle);
6101       angPrev = angCur;
6102     }
6103     Angles.clear();
6104     it = res.begin();
6105     for(; it!=res.end(); it++)
6106       Angles.push_back( *it );
6107   }
6108 }
6109
6110
6111 //================================================================================
6112 /*!
6113  * \brief Move or copy theElements applying theTrsf to their nodes
6114  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6115  *  \param theTrsf - transformation to apply
6116  *  \param theCopy - if true, create translated copies of theElems
6117  *  \param theMakeGroups - if true and theCopy, create translated groups
6118  *  \param theTargetMesh - mesh to copy translated elements into
6119  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6120  */
6121 //================================================================================
6122
6123 SMESH_MeshEditor::PGroupIDs
6124 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6125                              const gp_Trsf&     theTrsf,
6126                              const bool         theCopy,
6127                              const bool         theMakeGroups,
6128                              SMESH_Mesh*        theTargetMesh)
6129 {
6130   myLastCreatedElems.Clear();
6131   myLastCreatedNodes.Clear();
6132
6133   bool needReverse = false;
6134   string groupPostfix;
6135   switch ( theTrsf.Form() ) {
6136   case gp_PntMirror:
6137     MESSAGE("gp_PntMirror");
6138     needReverse = true;
6139     groupPostfix = "mirrored";
6140     break;
6141   case gp_Ax1Mirror:
6142     MESSAGE("gp_Ax1Mirror");
6143     groupPostfix = "mirrored";
6144     break;
6145   case gp_Ax2Mirror:
6146     MESSAGE("gp_Ax2Mirror");
6147     needReverse = true;
6148     groupPostfix = "mirrored";
6149     break;
6150   case gp_Rotation:
6151     MESSAGE("gp_Rotation");
6152     groupPostfix = "rotated";
6153     break;
6154   case gp_Translation:
6155     MESSAGE("gp_Translation");
6156     groupPostfix = "translated";
6157     break;
6158   case gp_Scale:
6159     MESSAGE("gp_Scale");
6160     groupPostfix = "scaled";
6161     break;
6162   case gp_CompoundTrsf: // different scale by axis
6163     MESSAGE("gp_CompoundTrsf");
6164     groupPostfix = "scaled";
6165     break;
6166   default:
6167     MESSAGE("default");
6168     needReverse = false;
6169     groupPostfix = "transformed";
6170   }
6171
6172   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6173   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6174   SMESHDS_Mesh* aMesh    = GetMeshDS();
6175
6176
6177   // map old node to new one
6178   TNodeNodeMap nodeMap;
6179
6180   // elements sharing moved nodes; those of them which have all
6181   // nodes mirrored but are not in theElems are to be reversed
6182   TIDSortedElemSet inverseElemSet;
6183
6184   // source elements for each generated one
6185   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6186
6187   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6188   TIDSortedElemSet orphanNode;
6189
6190   if ( theElems.empty() ) // transform the whole mesh
6191   {
6192     // add all elements
6193     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6194     while ( eIt->more() ) theElems.insert( eIt->next() );
6195     // add orphan nodes
6196     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6197     while ( nIt->more() )
6198     {
6199       const SMDS_MeshNode* node = nIt->next();
6200       if ( node->NbInverseElements() == 0)
6201         orphanNode.insert( node );
6202     }
6203   }
6204
6205   // loop on elements to transform nodes : first orphan nodes then elems
6206   TIDSortedElemSet::iterator itElem;
6207   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6208   for (int i=0; i<2; i++)
6209   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6210     const SMDS_MeshElement* elem = *itElem;
6211     if ( !elem )
6212       continue;
6213
6214     // loop on elem nodes
6215     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6216     while ( itN->more() ) {
6217
6218       const SMDS_MeshNode* node = cast2Node( itN->next() );
6219       // check if a node has been already transformed
6220       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6221         nodeMap.insert( make_pair ( node, node ));
6222       if ( !n2n_isnew.second )
6223         continue;
6224
6225       double coord[3];
6226       coord[0] = node->X();
6227       coord[1] = node->Y();
6228       coord[2] = node->Z();
6229       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6230       if ( theTargetMesh ) {
6231         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6232         n2n_isnew.first->second = newNode;
6233         myLastCreatedNodes.Append(newNode);
6234         srcNodes.Append( node );
6235       }
6236       else if ( theCopy ) {
6237         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6238         n2n_isnew.first->second = newNode;
6239         myLastCreatedNodes.Append(newNode);
6240         srcNodes.Append( node );
6241       }
6242       else {
6243         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6244         // node position on shape becomes invalid
6245         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6246           ( SMDS_SpacePosition::originSpacePosition() );
6247       }
6248
6249       // keep inverse elements
6250       if ( !theCopy && !theTargetMesh && needReverse ) {
6251         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6252         while ( invElemIt->more() ) {
6253           const SMDS_MeshElement* iel = invElemIt->next();
6254           inverseElemSet.insert( iel );
6255         }
6256       }
6257     }
6258   }
6259
6260   // either create new elements or reverse mirrored ones
6261   if ( !theCopy && !needReverse && !theTargetMesh )
6262     return PGroupIDs();
6263
6264   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6265   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6266     theElems.insert( *invElemIt );
6267
6268   // Replicate or reverse elements
6269
6270   std::vector<int> iForw;
6271   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6272   {
6273     const SMDS_MeshElement* elem = *itElem;
6274     if ( !elem ) continue;
6275
6276     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6277     int                  nbNodes  = elem->NbNodes();
6278     if ( geomType == SMDSGeom_NONE ) continue; // node
6279
6280     switch ( geomType ) {
6281
6282     case SMDSGeom_POLYGON:  // ---------------------- polygon
6283       {
6284         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6285         int iNode = 0;
6286         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6287         while (itN->more()) {
6288           const SMDS_MeshNode* node =
6289             static_cast<const SMDS_MeshNode*>(itN->next());
6290           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6291           if (nodeMapIt == nodeMap.end())
6292             break; // not all nodes transformed
6293           if (needReverse) {
6294             // reverse mirrored faces and volumes
6295             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6296           } else {
6297             poly_nodes[iNode] = (*nodeMapIt).second;
6298           }
6299           iNode++;
6300         }
6301         if ( iNode != nbNodes )
6302           continue; // not all nodes transformed
6303
6304         if ( theTargetMesh ) {
6305           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6306           srcElems.Append( elem );
6307         }
6308         else if ( theCopy ) {
6309           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6310           srcElems.Append( elem );
6311         }
6312         else {
6313           aMesh->ChangePolygonNodes(elem, poly_nodes);
6314         }
6315       }
6316       break;
6317
6318     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6319       {
6320         const SMDS_VtkVolume* aPolyedre =
6321           dynamic_cast<const SMDS_VtkVolume*>( elem );
6322         if (!aPolyedre) {
6323           MESSAGE("Warning: bad volumic element");
6324           continue;
6325         }
6326
6327         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6328         vector<int> quantities; quantities.reserve( nbNodes );
6329
6330         bool allTransformed = true;
6331         int nbFaces = aPolyedre->NbFaces();
6332         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6333           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6334           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6335             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6336             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6337             if (nodeMapIt == nodeMap.end()) {
6338               allTransformed = false; // not all nodes transformed
6339             } else {
6340               poly_nodes.push_back((*nodeMapIt).second);
6341             }
6342             if ( needReverse && allTransformed )
6343               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6344           }
6345           quantities.push_back(nbFaceNodes);
6346         }
6347         if ( !allTransformed )
6348           continue; // not all nodes transformed
6349
6350         if ( theTargetMesh ) {
6351           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6352           srcElems.Append( elem );
6353         }
6354         else if ( theCopy ) {
6355           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6356           srcElems.Append( elem );
6357         }
6358         else {
6359           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6360         }
6361       }
6362       break;
6363
6364     case SMDSGeom_BALL: // -------------------- Ball
6365       {
6366         if ( !theCopy && !theTargetMesh ) continue;
6367
6368         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6369         if (nodeMapIt == nodeMap.end())
6370           continue; // not all nodes transformed
6371
6372         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6373         if ( theTargetMesh ) {
6374           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6375           srcElems.Append( elem );
6376         }
6377         else {
6378           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6379           srcElems.Append( elem );
6380         }
6381       }
6382       break;
6383
6384     default: // ----------------------- Regular elements
6385
6386       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6387       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6388       const std::vector<int>& i = needReverse ? iRev : iForw;
6389
6390       // find transformed nodes
6391       vector<const SMDS_MeshNode*> nodes(nbNodes);
6392       int iNode = 0;
6393       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6394       while ( itN->more() ) {
6395         const SMDS_MeshNode* node =
6396           static_cast<const SMDS_MeshNode*>( itN->next() );
6397         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6398         if ( nodeMapIt == nodeMap.end() )
6399           break; // not all nodes transformed
6400         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6401       }
6402       if ( iNode != nbNodes )
6403         continue; // not all nodes transformed
6404
6405       if ( theTargetMesh ) {
6406         if ( SMDS_MeshElement* copy =
6407              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6408           myLastCreatedElems.Append( copy );
6409           srcElems.Append( elem );
6410         }
6411       }
6412       else if ( theCopy ) {
6413         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6414           srcElems.Append( elem );
6415       }
6416       else {
6417         // reverse element as it was reversed by transformation
6418         if ( nbNodes > 2 )
6419           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6420       }
6421     } // switch ( geomType )
6422
6423   } // loop on elements
6424
6425   PGroupIDs newGroupIDs;
6426
6427   if ( ( theMakeGroups && theCopy ) ||
6428        ( theMakeGroups && theTargetMesh ) )
6429     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6430
6431   return newGroupIDs;
6432 }
6433
6434 //=======================================================================
6435 /*!
6436  * \brief Create groups of elements made during transformation
6437  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6438  *  \param elemGens - elements making corresponding myLastCreatedElems
6439  *  \param postfix - to append to names of new groups
6440  *  \param targetMesh - mesh to create groups in
6441  *  \param topPresent - is there "top" elements that are created by sweeping
6442  */
6443 //=======================================================================
6444
6445 SMESH_MeshEditor::PGroupIDs
6446 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6447                                  const SMESH_SequenceOfElemPtr& elemGens,
6448                                  const std::string&             postfix,
6449                                  SMESH_Mesh*                    targetMesh,
6450                                  const bool                     topPresent)
6451 {
6452   PGroupIDs newGroupIDs( new list<int> );
6453   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6454
6455   // Sort existing groups by types and collect their names
6456
6457   // containers to store an old group and generated new ones;
6458   // 1st new group is for result elems of different type than a source one;
6459   // 2nd new group is for same type result elems ("top" group at extrusion)
6460   using boost::tuple;
6461   using boost::make_tuple;
6462   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6463   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6464   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6465   // group names
6466   set< string > groupNames;
6467
6468   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6469   if ( !groupIt->more() ) return newGroupIDs;
6470
6471   int newGroupID = mesh->GetGroupIds().back()+1;
6472   while ( groupIt->more() )
6473   {
6474     SMESH_Group * group = groupIt->next();
6475     if ( !group ) continue;
6476     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6477     if ( !groupDS || groupDS->IsEmpty() ) continue;
6478     groupNames.insert    ( group->GetName() );
6479     groupDS->SetStoreName( group->GetName() );
6480     const SMDSAbs_ElementType type = groupDS->GetType();
6481     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6482     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6483     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6484     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6485   }
6486
6487   // Loop on nodes and elements to add them in new groups
6488
6489   vector< const SMDS_MeshElement* > resultElems;
6490   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6491   {
6492     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6493     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6494     if ( gens.Length() != elems.Length() )
6495       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6496
6497     // loop on created elements
6498     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6499     {
6500       const SMDS_MeshElement* sourceElem = gens( iElem );
6501       if ( !sourceElem ) {
6502         MESSAGE("generateGroups(): NULL source element");
6503         continue;
6504       }
6505       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6506       if ( groupsOldNew.empty() ) { // no groups of this type at all
6507         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6508           ++iElem; // skip all elements made by sourceElem
6509         continue;
6510       }
6511       // collect all elements made by the iElem-th sourceElem
6512       resultElems.clear();
6513       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6514         if ( resElem != sourceElem )
6515           resultElems.push_back( resElem );
6516       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6517         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6518           if ( resElem != sourceElem )
6519             resultElems.push_back( resElem );
6520
6521       const SMDS_MeshElement* topElem = 0;
6522       if ( isNodes ) // there must be a top element
6523       {
6524         topElem = resultElems.back();
6525         resultElems.pop_back();
6526       }
6527       else
6528       {
6529         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6530         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6531           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6532           {
6533             topElem = *resElemIt;
6534             *resElemIt = 0; // erase *resElemIt
6535             break;
6536           }
6537       }
6538       // add resultElems to groups originted from ones the sourceElem belongs to
6539       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6540       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6541       {
6542         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6543         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6544         {
6545           // fill in a new group
6546           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6547           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6548           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6549             if ( *resElemIt )
6550               newGroup.Add( *resElemIt );
6551
6552           // fill a "top" group
6553           if ( topElem )
6554           {
6555             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6556             newTopGroup.Add( topElem );
6557          }
6558         }
6559       }
6560     } // loop on created elements
6561   }// loop on nodes and elements
6562
6563   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6564
6565   list<int> topGrouIds;
6566   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6567   {
6568     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6569     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6570                                       orderedOldNewGroups[i]->get<2>() };
6571     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6572     {
6573       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6574       if ( newGroupDS->IsEmpty() )
6575       {
6576         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6577       }
6578       else
6579       {
6580         // set group type
6581         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6582
6583         // make a name
6584         const bool isTop = ( topPresent &&
6585                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6586                              is2nd );
6587
6588         string name = oldGroupDS->GetStoreName();
6589         { // remove trailing whitespaces (issue 22599)
6590           size_t size = name.size();
6591           while ( size > 1 && isspace( name[ size-1 ]))
6592             --size;
6593           if ( size != name.size() )
6594           {
6595             name.resize( size );
6596             oldGroupDS->SetStoreName( name.c_str() );
6597           }
6598         }
6599         if ( !targetMesh ) {
6600           string suffix = ( isTop ? "top": postfix.c_str() );
6601           name += "_";
6602           name += suffix;
6603           int nb = 1;
6604           while ( !groupNames.insert( name ).second ) // name exists
6605             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6606         }
6607         else if ( isTop ) {
6608           name += "_top";
6609         }
6610         newGroupDS->SetStoreName( name.c_str() );
6611
6612         // make a SMESH_Groups
6613         mesh->AddGroup( newGroupDS );
6614         if ( isTop )
6615           topGrouIds.push_back( newGroupDS->GetID() );
6616         else
6617           newGroupIDs->push_back( newGroupDS->GetID() );
6618       }
6619     }
6620   }
6621   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6622
6623   return newGroupIDs;
6624 }
6625
6626 //================================================================================
6627 /*!
6628  * \brief Return list of group of nodes close to each other within theTolerance
6629  *        Search among theNodes or in the whole mesh if theNodes is empty using
6630  *        an Octree algorithm
6631  */
6632 //================================================================================
6633
6634 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6635                                             const double         theTolerance,
6636                                             TListOfListOfNodes & theGroupsOfNodes)
6637 {
6638   myLastCreatedElems.Clear();
6639   myLastCreatedNodes.Clear();
6640
6641   if ( theNodes.empty() )
6642   { // get all nodes in the mesh
6643     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6644     while ( nIt->more() )
6645       theNodes.insert( theNodes.end(),nIt->next());
6646   }
6647
6648   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6649 }
6650
6651 //=======================================================================
6652 //function : SimplifyFace
6653 //purpose  :
6654 //=======================================================================
6655
6656 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6657                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6658                                     vector<int>&                         quantities) const
6659 {
6660   int nbNodes = faceNodes.size();
6661
6662   if (nbNodes < 3)
6663     return 0;
6664
6665   set<const SMDS_MeshNode*> nodeSet;
6666
6667   // get simple seq of nodes
6668   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6669   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6670   int iSimple = 0, nbUnique = 0;
6671
6672   simpleNodes[iSimple++] = faceNodes[0];
6673   nbUnique++;
6674   for (int iCur = 1; iCur < nbNodes; iCur++) {
6675     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6676       simpleNodes[iSimple++] = faceNodes[iCur];
6677       if (nodeSet.insert( faceNodes[iCur] ).second)
6678         nbUnique++;
6679     }
6680   }
6681   int nbSimple = iSimple;
6682   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6683     nbSimple--;
6684     iSimple--;
6685   }
6686
6687   if (nbUnique < 3)
6688     return 0;
6689
6690   // separate loops
6691   int nbNew = 0;
6692   bool foundLoop = (nbSimple > nbUnique);
6693   while (foundLoop) {
6694     foundLoop = false;
6695     set<const SMDS_MeshNode*> loopSet;
6696     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6697       const SMDS_MeshNode* n = simpleNodes[iSimple];
6698       if (!loopSet.insert( n ).second) {
6699         foundLoop = true;
6700
6701         // separate loop
6702         int iC = 0, curLast = iSimple;
6703         for (; iC < curLast; iC++) {
6704           if (simpleNodes[iC] == n) break;
6705         }
6706         int loopLen = curLast - iC;
6707         if (loopLen > 2) {
6708           // create sub-element
6709           nbNew++;
6710           quantities.push_back(loopLen);
6711           for (; iC < curLast; iC++) {
6712             poly_nodes.push_back(simpleNodes[iC]);
6713           }
6714         }
6715         // shift the rest nodes (place from the first loop position)
6716         for (iC = curLast + 1; iC < nbSimple; iC++) {
6717           simpleNodes[iC - loopLen] = simpleNodes[iC];
6718         }
6719         nbSimple -= loopLen;
6720         iSimple -= loopLen;
6721       }
6722     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6723   } // while (foundLoop)
6724
6725   if (iSimple > 2) {
6726     nbNew++;
6727     quantities.push_back(iSimple);
6728     for (int i = 0; i < iSimple; i++)
6729       poly_nodes.push_back(simpleNodes[i]);
6730   }
6731
6732   return nbNew;
6733 }
6734
6735 //=======================================================================
6736 //function : MergeNodes
6737 //purpose  : In each group, the cdr of nodes are substituted by the first one
6738 //           in all elements.
6739 //=======================================================================
6740
6741 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6742 {
6743   MESSAGE("MergeNodes");
6744   myLastCreatedElems.Clear();
6745   myLastCreatedNodes.Clear();
6746
6747   SMESHDS_Mesh* aMesh = GetMeshDS();
6748
6749   TNodeNodeMap nodeNodeMap; // node to replace - new node
6750   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6751   list< int > rmElemIds, rmNodeIds;
6752
6753   // Fill nodeNodeMap and elems
6754
6755   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6756   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6757     list<const SMDS_MeshNode*>& nodes = *grIt;
6758     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6759     const SMDS_MeshNode* nToKeep = *nIt;
6760     //MESSAGE("node to keep " << nToKeep->GetID());
6761     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6762       const SMDS_MeshNode* nToRemove = *nIt;
6763       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6764       if ( nToRemove != nToKeep ) {
6765         //MESSAGE("  node to remove " << nToRemove->GetID());
6766         rmNodeIds.push_back( nToRemove->GetID() );
6767         AddToSameGroups( nToKeep, nToRemove, aMesh );
6768         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6769         // after MergeNodes() w/o creating node in place of merged ones.
6770         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6771         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6772           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6773             sm->SetIsAlwaysComputed( true );
6774       }
6775
6776       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6777       while ( invElemIt->more() ) {
6778         const SMDS_MeshElement* elem = invElemIt->next();
6779         elems.insert(elem);
6780       }
6781     }
6782   }
6783   // Change element nodes or remove an element
6784
6785   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6786   for ( ; eIt != elems.end(); eIt++ ) {
6787     const SMDS_MeshElement* elem = *eIt;
6788     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6789     int nbNodes = elem->NbNodes();
6790     int aShapeId = FindShape( elem );
6791
6792     set<const SMDS_MeshNode*> nodeSet;
6793     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6794     int iUnique = 0, iCur = 0, nbRepl = 0;
6795     vector<int> iRepl( nbNodes );
6796
6797     // get new seq of nodes
6798     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6799     while ( itN->more() ) {
6800       const SMDS_MeshNode* n =
6801         static_cast<const SMDS_MeshNode*>( itN->next() );
6802
6803       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6804       if ( nnIt != nodeNodeMap.end() ) { // n sticks
6805         n = (*nnIt).second;
6806         // BUG 0020185: begin
6807         {
6808           bool stopRecur = false;
6809           set<const SMDS_MeshNode*> nodesRecur;
6810           nodesRecur.insert(n);
6811           while (!stopRecur) {
6812             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6813             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6814               n = (*nnIt_i).second;
6815               if (!nodesRecur.insert(n).second) {
6816                 // error: recursive dependancy
6817                 stopRecur = true;
6818               }
6819             }
6820             else
6821               stopRecur = true;
6822           }
6823         }
6824         // BUG 0020185: end
6825       }
6826       curNodes[ iCur ] = n;
6827       bool isUnique = nodeSet.insert( n ).second;
6828       if ( isUnique )
6829         uniqueNodes[ iUnique++ ] = n;
6830       else
6831         iRepl[ nbRepl++ ] = iCur;
6832       iCur++;
6833     }
6834
6835     // Analyse element topology after replacement
6836
6837     bool isOk = true;
6838     int nbUniqueNodes = nodeSet.size();
6839     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6840     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6841       // Polygons and Polyhedral volumes
6842       if (elem->IsPoly()) {
6843
6844         if (elem->GetType() == SMDSAbs_Face) {
6845           // Polygon
6846           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6847           int inode = 0;
6848           for (; inode < nbNodes; inode++) {
6849             face_nodes[inode] = curNodes[inode];
6850           }
6851
6852           vector<const SMDS_MeshNode *> polygons_nodes;
6853           vector<int> quantities;
6854           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6855           if (nbNew > 0) {
6856             inode = 0;
6857             for (int iface = 0; iface < nbNew; iface++) {
6858               int nbNodes = quantities[iface];
6859               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6860               for (int ii = 0; ii < nbNodes; ii++, inode++) {
6861                 poly_nodes[ii] = polygons_nodes[inode];
6862               }
6863               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6864               myLastCreatedElems.Append(newElem);
6865               if (aShapeId)
6866                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6867             }
6868
6869             MESSAGE("ChangeElementNodes MergeNodes Polygon");
6870             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6871             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6872             int quid =0;
6873             if (nbNew > 0) quid = nbNew - 1;
6874             vector<int> newquant(quantities.begin()+quid, quantities.end());
6875             const SMDS_MeshElement* newElem = 0;
6876             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6877             myLastCreatedElems.Append(newElem);
6878             if ( aShapeId && newElem )
6879               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6880             rmElemIds.push_back(elem->GetID());
6881           }
6882           else {
6883             rmElemIds.push_back(elem->GetID());
6884           }
6885
6886         }
6887         else if (elem->GetType() == SMDSAbs_Volume) {
6888           // Polyhedral volume
6889           if (nbUniqueNodes < 4) {
6890             rmElemIds.push_back(elem->GetID());
6891           }
6892           else {
6893             // each face has to be analyzed in order to check volume validity
6894             const SMDS_VtkVolume* aPolyedre =
6895               dynamic_cast<const SMDS_VtkVolume*>( elem );
6896             if (aPolyedre) {
6897               int nbFaces = aPolyedre->NbFaces();
6898
6899               vector<const SMDS_MeshNode *> poly_nodes;
6900               vector<int> quantities;
6901
6902               for (int iface = 1; iface <= nbFaces; iface++) {
6903                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6904                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6905
6906                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6907                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6908                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6909                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6910                     faceNode = (*nnIt).second;
6911                   }
6912                   faceNodes[inode - 1] = faceNode;
6913                 }
6914
6915                 SimplifyFace(faceNodes, poly_nodes, quantities);
6916               }
6917
6918               if (quantities.size() > 3) {
6919                 // to be done: remove coincident faces
6920               }
6921
6922               if (quantities.size() > 3)
6923                 {
6924                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6925                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6926                   const SMDS_MeshElement* newElem = 0;
6927                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6928                   myLastCreatedElems.Append(newElem);
6929                   if ( aShapeId && newElem )
6930                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
6931                   rmElemIds.push_back(elem->GetID());
6932                 }
6933             }
6934             else {
6935               rmElemIds.push_back(elem->GetID());
6936             }
6937           }
6938         }
6939         else {
6940         }
6941
6942         continue;
6943       } // poly element
6944
6945       // Regular elements
6946       // TODO not all the possible cases are solved. Find something more generic?
6947       switch ( nbNodes ) {
6948       case 2: ///////////////////////////////////// EDGE
6949         isOk = false; break;
6950       case 3: ///////////////////////////////////// TRIANGLE
6951         isOk = false; break;
6952       case 4:
6953         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6954           isOk = false;
6955         else { //////////////////////////////////// QUADRANGLE
6956           if ( nbUniqueNodes < 3 )
6957             isOk = false;
6958           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6959             isOk = false; // opposite nodes stick
6960           //MESSAGE("isOk " << isOk);
6961         }
6962         break;
6963       case 6: ///////////////////////////////////// PENTAHEDRON
6964         if ( nbUniqueNodes == 4 ) {
6965           // ---------------------------------> tetrahedron
6966           if (nbRepl == 3 &&
6967               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6968             // all top nodes stick: reverse a bottom
6969             uniqueNodes[ 0 ] = curNodes [ 1 ];
6970             uniqueNodes[ 1 ] = curNodes [ 0 ];
6971           }
6972           else if (nbRepl == 3 &&
6973                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6974             // all bottom nodes stick: set a top before
6975             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6976             uniqueNodes[ 0 ] = curNodes [ 3 ];
6977             uniqueNodes[ 1 ] = curNodes [ 4 ];
6978             uniqueNodes[ 2 ] = curNodes [ 5 ];
6979           }
6980           else if (nbRepl == 4 &&
6981                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6982             // a lateral face turns into a line: reverse a bottom
6983             uniqueNodes[ 0 ] = curNodes [ 1 ];
6984             uniqueNodes[ 1 ] = curNodes [ 0 ];
6985           }
6986           else
6987             isOk = false;
6988         }
6989         else if ( nbUniqueNodes == 5 ) {
6990           // PENTAHEDRON --------------------> 2 tetrahedrons
6991           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6992             // a bottom node sticks with a linked top one
6993             // 1.
6994             SMDS_MeshElement* newElem =
6995               aMesh->AddVolume(curNodes[ 3 ],
6996                                curNodes[ 4 ],
6997                                curNodes[ 5 ],
6998                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6999             myLastCreatedElems.Append(newElem);
7000             if ( aShapeId )
7001               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7002             // 2. : reverse a bottom
7003             uniqueNodes[ 0 ] = curNodes [ 1 ];
7004             uniqueNodes[ 1 ] = curNodes [ 0 ];
7005             nbUniqueNodes = 4;
7006           }
7007           else
7008             isOk = false;
7009         }
7010         else
7011           isOk = false;
7012         break;
7013       case 8: {
7014         if(elem->IsQuadratic()) { // Quadratic quadrangle
7015           //   1    5    2
7016           //    +---+---+
7017           //    |       |
7018           //    |       |
7019           //   4+       +6
7020           //    |       |
7021           //    |       |
7022           //    +---+---+
7023           //   0    7    3
7024           isOk = false;
7025           if(nbRepl==2) {
7026             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7027           }
7028           if(nbRepl==3) {
7029             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7030             nbUniqueNodes = 6;
7031             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7032               uniqueNodes[0] = curNodes[0];
7033               uniqueNodes[1] = curNodes[2];
7034               uniqueNodes[2] = curNodes[3];
7035               uniqueNodes[3] = curNodes[5];
7036               uniqueNodes[4] = curNodes[6];
7037               uniqueNodes[5] = curNodes[7];
7038               isOk = true;
7039             }
7040             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7041               uniqueNodes[0] = curNodes[0];
7042               uniqueNodes[1] = curNodes[1];
7043               uniqueNodes[2] = curNodes[2];
7044               uniqueNodes[3] = curNodes[4];
7045               uniqueNodes[4] = curNodes[5];
7046               uniqueNodes[5] = curNodes[6];
7047               isOk = true;
7048             }
7049             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7050               uniqueNodes[0] = curNodes[1];
7051               uniqueNodes[1] = curNodes[2];
7052               uniqueNodes[2] = curNodes[3];
7053               uniqueNodes[3] = curNodes[5];
7054               uniqueNodes[4] = curNodes[6];
7055               uniqueNodes[5] = curNodes[0];
7056               isOk = true;
7057             }
7058             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7059               uniqueNodes[0] = curNodes[0];
7060               uniqueNodes[1] = curNodes[1];
7061               uniqueNodes[2] = curNodes[3];
7062               uniqueNodes[3] = curNodes[4];
7063               uniqueNodes[4] = curNodes[6];
7064               uniqueNodes[5] = curNodes[7];
7065               isOk = true;
7066             }
7067             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7068               uniqueNodes[0] = curNodes[0];
7069               uniqueNodes[1] = curNodes[2];
7070               uniqueNodes[2] = curNodes[3];
7071               uniqueNodes[3] = curNodes[1];
7072               uniqueNodes[4] = curNodes[6];
7073               uniqueNodes[5] = curNodes[7];
7074               isOk = true;
7075             }
7076             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7077               uniqueNodes[0] = curNodes[0];
7078               uniqueNodes[1] = curNodes[1];
7079               uniqueNodes[2] = curNodes[2];
7080               uniqueNodes[3] = curNodes[4];
7081               uniqueNodes[4] = curNodes[5];
7082               uniqueNodes[5] = curNodes[7];
7083               isOk = true;
7084             }
7085             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7086               uniqueNodes[0] = curNodes[0];
7087               uniqueNodes[1] = curNodes[1];
7088               uniqueNodes[2] = curNodes[3];
7089               uniqueNodes[3] = curNodes[4];
7090               uniqueNodes[4] = curNodes[2];
7091               uniqueNodes[5] = curNodes[7];
7092               isOk = true;
7093             }
7094             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7095               uniqueNodes[0] = curNodes[0];
7096               uniqueNodes[1] = curNodes[1];
7097               uniqueNodes[2] = curNodes[2];
7098               uniqueNodes[3] = curNodes[4];
7099               uniqueNodes[4] = curNodes[5];
7100               uniqueNodes[5] = curNodes[3];
7101               isOk = true;
7102             }
7103           }
7104           if(nbRepl==4) {
7105             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7106           }
7107           if(nbRepl==5) {
7108             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7109           }
7110           break;
7111         }
7112         //////////////////////////////////// HEXAHEDRON
7113         isOk = false;
7114         SMDS_VolumeTool hexa (elem);
7115         hexa.SetExternalNormal();
7116         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7117           //////////////////////// HEX ---> 1 tetrahedron
7118           for ( int iFace = 0; iFace < 6; iFace++ ) {
7119             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7120             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7121                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7122                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7123               // one face turns into a point ...
7124               int iOppFace = hexa.GetOppFaceIndex( iFace );
7125               ind = hexa.GetFaceNodesIndices( iOppFace );
7126               int nbStick = 0;
7127               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7128                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7129                   nbStick++;
7130               }
7131               if ( nbStick == 1 ) {
7132                 // ... and the opposite one - into a triangle.
7133                 // set a top node
7134                 ind = hexa.GetFaceNodesIndices( iFace );
7135                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7136                 isOk = true;
7137               }
7138               break;
7139             }
7140           }
7141         }
7142         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7143           //////////////////////// HEX ---> 1 prism
7144           int nbTria = 0, iTria[3];
7145           const int *ind; // indices of face nodes
7146           // look for triangular faces
7147           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7148             ind = hexa.GetFaceNodesIndices( iFace );
7149             TIDSortedNodeSet faceNodes;
7150             for ( iCur = 0; iCur < 4; iCur++ )
7151               faceNodes.insert( curNodes[ind[iCur]] );
7152             if ( faceNodes.size() == 3 )
7153               iTria[ nbTria++ ] = iFace;
7154           }
7155           // check if triangles are opposite
7156           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7157           {
7158             isOk = true;
7159             // set nodes of the bottom triangle
7160             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7161             vector<int> indB;
7162             for ( iCur = 0; iCur < 4; iCur++ )
7163               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7164                 indB.push_back( ind[iCur] );
7165             if ( !hexa.IsForward() )
7166               std::swap( indB[0], indB[2] );
7167             for ( iCur = 0; iCur < 3; iCur++ )
7168               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7169             // set nodes of the top triangle
7170             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7171             for ( iCur = 0; iCur < 3; ++iCur )
7172               for ( int j = 0; j < 4; ++j )
7173                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7174                 {
7175                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7176                   break;
7177                 }
7178           }
7179           break;
7180         }
7181         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7182           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7183           for ( int iFace = 0; iFace < 6; iFace++ ) {
7184             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7185             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7186                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7187                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7188               // one face turns into a point ...
7189               int iOppFace = hexa.GetOppFaceIndex( iFace );
7190               ind = hexa.GetFaceNodesIndices( iOppFace );
7191               int nbStick = 0;
7192               iUnique = 2;  // reverse a tetrahedron 1 bottom
7193               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7194                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7195                   nbStick++;
7196                 else if ( iUnique >= 0 )
7197                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7198               }
7199               if ( nbStick == 0 ) {
7200                 // ... and the opposite one is a quadrangle
7201                 // set a top node
7202                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7203                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7204                 nbUniqueNodes = 4;
7205                 // tetrahedron 2
7206                 SMDS_MeshElement* newElem =
7207                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7208                                    curNodes[ind[ 3 ]],
7209                                    curNodes[ind[ 2 ]],
7210                                    curNodes[indTop[ 0 ]]);
7211                 myLastCreatedElems.Append(newElem);
7212                 if ( aShapeId )
7213                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7214                 isOk = true;
7215               }
7216               break;
7217             }
7218           }
7219         }
7220         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7221           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7222           // find indices of quad and tri faces
7223           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7224           for ( iFace = 0; iFace < 6; iFace++ ) {
7225             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7226             nodeSet.clear();
7227             for ( iCur = 0; iCur < 4; iCur++ )
7228               nodeSet.insert( curNodes[ind[ iCur ]] );
7229             nbUniqueNodes = nodeSet.size();
7230             if ( nbUniqueNodes == 3 )
7231               iTriFace[ nbTri++ ] = iFace;
7232             else if ( nbUniqueNodes == 4 )
7233               iQuadFace[ nbQuad++ ] = iFace;
7234           }
7235           if (nbQuad == 2 && nbTri == 4 &&
7236               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7237             // 2 opposite quadrangles stuck with a diagonal;
7238             // sample groups of merged indices: (0-4)(2-6)
7239             // --------------------------------------------> 2 tetrahedrons
7240             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7241             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7242             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7243             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7244                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7245               // stuck with 0-2 diagonal
7246               i0  = ind1[ 3 ];
7247               i1d = ind1[ 0 ];
7248               i2  = ind1[ 1 ];
7249               i3d = ind1[ 2 ];
7250               i0t = ind2[ 1 ];
7251               i2t = ind2[ 3 ];
7252             }
7253             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7254                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7255               // stuck with 1-3 diagonal
7256               i0  = ind1[ 0 ];
7257               i1d = ind1[ 1 ];
7258               i2  = ind1[ 2 ];
7259               i3d = ind1[ 3 ];
7260               i0t = ind2[ 0 ];
7261               i2t = ind2[ 1 ];
7262             }
7263             else {
7264               ASSERT(0);
7265             }
7266             // tetrahedron 1
7267             uniqueNodes[ 0 ] = curNodes [ i0 ];
7268             uniqueNodes[ 1 ] = curNodes [ i1d ];
7269             uniqueNodes[ 2 ] = curNodes [ i3d ];
7270             uniqueNodes[ 3 ] = curNodes [ i0t ];
7271             nbUniqueNodes = 4;
7272             // tetrahedron 2
7273             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7274                                                          curNodes[ i2 ],
7275                                                          curNodes[ i3d ],
7276                                                          curNodes[ i2t ]);
7277             myLastCreatedElems.Append(newElem);
7278             if ( aShapeId )
7279               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7280             isOk = true;
7281           }
7282           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7283                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7284             // --------------------------------------------> prism
7285             // find 2 opposite triangles
7286             nbUniqueNodes = 6;
7287             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7288               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7289                 // find indices of kept and replaced nodes
7290                 // and fill unique nodes of 2 opposite triangles
7291                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7292                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7293                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7294                 // fill unique nodes
7295                 iUnique = 0;
7296                 isOk = true;
7297                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7298                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7299                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7300                   if ( n == nInit ) {
7301                     // iCur of a linked node of the opposite face (make normals co-directed):
7302                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7303                     // check that correspondent corners of triangles are linked
7304                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7305                       isOk = false;
7306                     else {
7307                       uniqueNodes[ iUnique ] = n;
7308                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7309                       iUnique++;
7310                     }
7311                   }
7312                 }
7313                 break;
7314               }
7315             }
7316           }
7317         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7318         else
7319         {
7320           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7321         }
7322         break;
7323       } // HEXAHEDRON
7324
7325       default:
7326         isOk = false;
7327       } // switch ( nbNodes )
7328
7329     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7330
7331     if ( isOk ) { // the elem remains valid after sticking nodes
7332       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7333       {
7334         // Change nodes of polyedre
7335         const SMDS_VtkVolume* aPolyedre =
7336           dynamic_cast<const SMDS_VtkVolume*>( elem );
7337         if (aPolyedre) {
7338           int nbFaces = aPolyedre->NbFaces();
7339
7340           vector<const SMDS_MeshNode *> poly_nodes;
7341           vector<int> quantities (nbFaces);
7342
7343           for (int iface = 1; iface <= nbFaces; iface++) {
7344             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7345             quantities[iface - 1] = nbFaceNodes;
7346
7347             for (inode = 1; inode <= nbFaceNodes; inode++) {
7348               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7349
7350               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7351               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7352                 curNode = (*nnIt).second;
7353               }
7354               poly_nodes.push_back(curNode);
7355             }
7356           }
7357           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7358         }
7359       }
7360       else // replace non-polyhedron elements
7361       {
7362         const SMDSAbs_ElementType etyp = elem->GetType();
7363         const int elemId               = elem->GetID();
7364         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7365         uniqueNodes.resize(nbUniqueNodes);
7366
7367         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7368
7369         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7370         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7371         if ( sm && newElem )
7372           sm->AddElement( newElem );
7373         if ( elem != newElem )
7374           ReplaceElemInGroups( elem, newElem, aMesh );
7375       }
7376     }
7377     else {
7378       // Remove invalid regular element or invalid polygon
7379       rmElemIds.push_back( elem->GetID() );
7380     }
7381
7382   } // loop on elements
7383
7384   // Remove bad elements, then equal nodes (order important)
7385
7386   Remove( rmElemIds, false );
7387   Remove( rmNodeIds, true );
7388
7389 }
7390
7391
7392 // ========================================================
7393 // class   : SortableElement
7394 // purpose : allow sorting elements basing on their nodes
7395 // ========================================================
7396 class SortableElement : public set <const SMDS_MeshElement*>
7397 {
7398 public:
7399
7400   SortableElement( const SMDS_MeshElement* theElem )
7401   {
7402     myElem = theElem;
7403     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7404     while ( nodeIt->more() )
7405       this->insert( nodeIt->next() );
7406   }
7407
7408   const SMDS_MeshElement* Get() const
7409   { return myElem; }
7410
7411   void Set(const SMDS_MeshElement* e) const
7412   { myElem = e; }
7413
7414
7415 private:
7416   mutable const SMDS_MeshElement* myElem;
7417 };
7418
7419 //=======================================================================
7420 //function : FindEqualElements
7421 //purpose  : Return list of group of elements built on the same nodes.
7422 //           Search among theElements or in the whole mesh if theElements is empty
7423 //=======================================================================
7424
7425 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7426                                          TListOfListOfElementsID & theGroupsOfElementsID)
7427 {
7428   myLastCreatedElems.Clear();
7429   myLastCreatedNodes.Clear();
7430
7431   typedef map< SortableElement, int > TMapOfNodeSet;
7432   typedef list<int> TGroupOfElems;
7433
7434   if ( theElements.empty() )
7435   { // get all elements in the mesh
7436     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7437     while ( eIt->more() )
7438       theElements.insert( theElements.end(), eIt->next());
7439   }
7440
7441   vector< TGroupOfElems > arrayOfGroups;
7442   TGroupOfElems groupOfElems;
7443   TMapOfNodeSet mapOfNodeSet;
7444
7445   TIDSortedElemSet::iterator elemIt = theElements.begin();
7446   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7447     const SMDS_MeshElement* curElem = *elemIt;
7448     SortableElement SE(curElem);
7449     int ind = -1;
7450     // check uniqueness
7451     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7452     if( !(pp.second) ) {
7453       TMapOfNodeSet::iterator& itSE = pp.first;
7454       ind = (*itSE).second;
7455       arrayOfGroups[ind].push_back(curElem->GetID());
7456     }
7457     else {
7458       groupOfElems.clear();
7459       groupOfElems.push_back(curElem->GetID());
7460       arrayOfGroups.push_back(groupOfElems);
7461       i++;
7462     }
7463   }
7464
7465   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7466   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7467     groupOfElems = *groupIt;
7468     if ( groupOfElems.size() > 1 ) {
7469       groupOfElems.sort();
7470       theGroupsOfElementsID.push_back(groupOfElems);
7471     }
7472   }
7473 }
7474
7475 //=======================================================================
7476 //function : MergeElements
7477 //purpose  : In each given group, substitute all elements by the first one.
7478 //=======================================================================
7479
7480 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7481 {
7482   myLastCreatedElems.Clear();
7483   myLastCreatedNodes.Clear();
7484
7485   typedef list<int> TListOfIDs;
7486   TListOfIDs rmElemIds; // IDs of elems to remove
7487
7488   SMESHDS_Mesh* aMesh = GetMeshDS();
7489
7490   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7491   while ( groupsIt != theGroupsOfElementsID.end() ) {
7492     TListOfIDs& aGroupOfElemID = *groupsIt;
7493     aGroupOfElemID.sort();
7494     int elemIDToKeep = aGroupOfElemID.front();
7495     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7496     aGroupOfElemID.pop_front();
7497     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7498     while ( idIt != aGroupOfElemID.end() ) {
7499       int elemIDToRemove = *idIt;
7500       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7501       // add the kept element in groups of removed one (PAL15188)
7502       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7503       rmElemIds.push_back( elemIDToRemove );
7504       ++idIt;
7505     }
7506     ++groupsIt;
7507   }
7508
7509   Remove( rmElemIds, false );
7510 }
7511
7512 //=======================================================================
7513 //function : MergeEqualElements
7514 //purpose  : Remove all but one of elements built on the same nodes.
7515 //=======================================================================
7516
7517 void SMESH_MeshEditor::MergeEqualElements()
7518 {
7519   TIDSortedElemSet aMeshElements; /* empty input ==
7520                                      to merge equal elements in the whole mesh */
7521   TListOfListOfElementsID aGroupsOfElementsID;
7522   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7523   MergeElements(aGroupsOfElementsID);
7524 }
7525
7526 //=======================================================================
7527 //function : findAdjacentFace
7528 //purpose  :
7529 //=======================================================================
7530
7531 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7532                                                 const SMDS_MeshNode* n2,
7533                                                 const SMDS_MeshElement* elem)
7534 {
7535   TIDSortedElemSet elemSet, avoidSet;
7536   if ( elem )
7537     avoidSet.insert ( elem );
7538   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7539 }
7540
7541 //=======================================================================
7542 //function : FindFreeBorder
7543 //purpose  :
7544 //=======================================================================
7545
7546 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7547
7548 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7549                                        const SMDS_MeshNode*             theSecondNode,
7550                                        const SMDS_MeshNode*             theLastNode,
7551                                        list< const SMDS_MeshNode* > &   theNodes,
7552                                        list< const SMDS_MeshElement* >& theFaces)
7553 {
7554   if ( !theFirstNode || !theSecondNode )
7555     return false;
7556   // find border face between theFirstNode and theSecondNode
7557   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7558   if ( !curElem )
7559     return false;
7560
7561   theFaces.push_back( curElem );
7562   theNodes.push_back( theFirstNode );
7563   theNodes.push_back( theSecondNode );
7564
7565   //vector<const SMDS_MeshNode*> nodes;
7566   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7567   TIDSortedElemSet foundElems;
7568   bool needTheLast = ( theLastNode != 0 );
7569
7570   while ( nStart != theLastNode ) {
7571     if ( nStart == theFirstNode )
7572       return !needTheLast;
7573
7574     // find all free border faces sharing form nStart
7575
7576     list< const SMDS_MeshElement* > curElemList;
7577     list< const SMDS_MeshNode* > nStartList;
7578     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7579     while ( invElemIt->more() ) {
7580       const SMDS_MeshElement* e = invElemIt->next();
7581       if ( e == curElem || foundElems.insert( e ).second ) {
7582         // get nodes
7583         int iNode = 0, nbNodes = e->NbNodes();
7584         //const SMDS_MeshNode* nodes[nbNodes+1];
7585         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7586
7587         if(e->IsQuadratic()) {
7588           const SMDS_VtkFace* F =
7589             dynamic_cast<const SMDS_VtkFace*>(e);
7590           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7591           // use special nodes iterator
7592           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7593           while( anIter->more() ) {
7594             nodes[ iNode++ ] = cast2Node(anIter->next());
7595           }
7596         }
7597         else {
7598           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7599           while ( nIt->more() )
7600             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7601         }
7602         nodes[ iNode ] = nodes[ 0 ];
7603         // check 2 links
7604         for ( iNode = 0; iNode < nbNodes; iNode++ )
7605           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7606                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7607               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7608           {
7609             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7610             curElemList.push_back( e );
7611           }
7612       }
7613     }
7614     // analyse the found
7615
7616     int nbNewBorders = curElemList.size();
7617     if ( nbNewBorders == 0 ) {
7618       // no free border furthermore
7619       return !needTheLast;
7620     }
7621     else if ( nbNewBorders == 1 ) {
7622       // one more element found
7623       nIgnore = nStart;
7624       nStart = nStartList.front();
7625       curElem = curElemList.front();
7626       theFaces.push_back( curElem );
7627       theNodes.push_back( nStart );
7628     }
7629     else {
7630       // several continuations found
7631       list< const SMDS_MeshElement* >::iterator curElemIt;
7632       list< const SMDS_MeshNode* >::iterator nStartIt;
7633       // check if one of them reached the last node
7634       if ( needTheLast ) {
7635         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7636              curElemIt!= curElemList.end();
7637              curElemIt++, nStartIt++ )
7638           if ( *nStartIt == theLastNode ) {
7639             theFaces.push_back( *curElemIt );
7640             theNodes.push_back( *nStartIt );
7641             return true;
7642           }
7643       }
7644       // find the best free border by the continuations
7645       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7646       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7647       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7648            curElemIt!= curElemList.end();
7649            curElemIt++, nStartIt++ )
7650       {
7651         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7652         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7653         // find one more free border
7654         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7655           cNL->clear();
7656           cFL->clear();
7657         }
7658         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7659           // choice: clear a worse one
7660           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7661           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7662           contNodes[ iWorse ].clear();
7663           contFaces[ iWorse ].clear();
7664         }
7665       }
7666       if ( contNodes[0].empty() && contNodes[1].empty() )
7667         return false;
7668
7669       // append the best free border
7670       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7671       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7672       theNodes.pop_back(); // remove nIgnore
7673       theNodes.pop_back(); // remove nStart
7674       theFaces.pop_back(); // remove curElem
7675       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7676       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7677       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7678       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7679       return true;
7680
7681     } // several continuations found
7682   } // while ( nStart != theLastNode )
7683
7684   return true;
7685 }
7686
7687 //=======================================================================
7688 //function : CheckFreeBorderNodes
7689 //purpose  : Return true if the tree nodes are on a free border
7690 //=======================================================================
7691
7692 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7693                                             const SMDS_MeshNode* theNode2,
7694                                             const SMDS_MeshNode* theNode3)
7695 {
7696   list< const SMDS_MeshNode* > nodes;
7697   list< const SMDS_MeshElement* > faces;
7698   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7699 }
7700
7701 //=======================================================================
7702 //function : SewFreeBorder
7703 //purpose  :
7704 //=======================================================================
7705
7706 SMESH_MeshEditor::Sew_Error
7707 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7708                                  const SMDS_MeshNode* theBordSecondNode,
7709                                  const SMDS_MeshNode* theBordLastNode,
7710                                  const SMDS_MeshNode* theSideFirstNode,
7711                                  const SMDS_MeshNode* theSideSecondNode,
7712                                  const SMDS_MeshNode* theSideThirdNode,
7713                                  const bool           theSideIsFreeBorder,
7714                                  const bool           toCreatePolygons,
7715                                  const bool           toCreatePolyedrs)
7716 {
7717   myLastCreatedElems.Clear();
7718   myLastCreatedNodes.Clear();
7719
7720   MESSAGE("::SewFreeBorder()");
7721   Sew_Error aResult = SEW_OK;
7722
7723   // ====================================
7724   //    find side nodes and elements
7725   // ====================================
7726
7727   list< const SMDS_MeshNode* > nSide[ 2 ];
7728   list< const SMDS_MeshElement* > eSide[ 2 ];
7729   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7730   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7731
7732   // Free border 1
7733   // --------------
7734   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7735                       nSide[0], eSide[0])) {
7736     MESSAGE(" Free Border 1 not found " );
7737     aResult = SEW_BORDER1_NOT_FOUND;
7738   }
7739   if (theSideIsFreeBorder) {
7740     // Free border 2
7741     // --------------
7742     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7743                         nSide[1], eSide[1])) {
7744       MESSAGE(" Free Border 2 not found " );
7745       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7746     }
7747   }
7748   if ( aResult != SEW_OK )
7749     return aResult;
7750
7751   if (!theSideIsFreeBorder) {
7752     // Side 2
7753     // --------------
7754
7755     // -------------------------------------------------------------------------
7756     // Algo:
7757     // 1. If nodes to merge are not coincident, move nodes of the free border
7758     //    from the coord sys defined by the direction from the first to last
7759     //    nodes of the border to the correspondent sys of the side 2
7760     // 2. On the side 2, find the links most co-directed with the correspondent
7761     //    links of the free border
7762     // -------------------------------------------------------------------------
7763
7764     // 1. Since sewing may break if there are volumes to split on the side 2,
7765     //    we wont move nodes but just compute new coordinates for them
7766     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7767     TNodeXYZMap nBordXYZ;
7768     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7769     list< const SMDS_MeshNode* >::iterator nBordIt;
7770
7771     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7772     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7773     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7774     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7775     double tol2 = 1.e-8;
7776     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7777     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7778       // Need node movement.
7779
7780       // find X and Z axes to create trsf
7781       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7782       gp_Vec X = Zs ^ Zb;
7783       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7784         // Zb || Zs
7785         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7786
7787       // coord systems
7788       gp_Ax3 toBordAx( Pb1, Zb, X );
7789       gp_Ax3 fromSideAx( Ps1, Zs, X );
7790       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7791       // set trsf
7792       gp_Trsf toBordSys, fromSide2Sys;
7793       toBordSys.SetTransformation( toBordAx );
7794       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7795       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7796
7797       // move
7798       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7799         const SMDS_MeshNode* n = *nBordIt;
7800         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7801         toBordSys.Transforms( xyz );
7802         fromSide2Sys.Transforms( xyz );
7803         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7804       }
7805     }
7806     else {
7807       // just insert nodes XYZ in the nBordXYZ map
7808       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7809         const SMDS_MeshNode* n = *nBordIt;
7810         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7811       }
7812     }
7813
7814     // 2. On the side 2, find the links most co-directed with the correspondent
7815     //    links of the free border
7816
7817     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7818     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7819     sideNodes.push_back( theSideFirstNode );
7820
7821     bool hasVolumes = false;
7822     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7823     set<long> foundSideLinkIDs, checkedLinkIDs;
7824     SMDS_VolumeTool volume;
7825     //const SMDS_MeshNode* faceNodes[ 4 ];
7826
7827     const SMDS_MeshNode*    sideNode;
7828     const SMDS_MeshElement* sideElem;
7829     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7830     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7831     nBordIt = bordNodes.begin();
7832     nBordIt++;
7833     // border node position and border link direction to compare with
7834     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7835     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7836     // choose next side node by link direction or by closeness to
7837     // the current border node:
7838     bool searchByDir = ( *nBordIt != theBordLastNode );
7839     do {
7840       // find the next node on the Side 2
7841       sideNode = 0;
7842       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7843       long linkID;
7844       checkedLinkIDs.clear();
7845       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7846
7847       // loop on inverse elements of current node (prevSideNode) on the Side 2
7848       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7849       while ( invElemIt->more() )
7850       {
7851         const SMDS_MeshElement* elem = invElemIt->next();
7852         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7853         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7854         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7855         bool isVolume = volume.Set( elem );
7856         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7857         if ( isVolume ) // --volume
7858           hasVolumes = true;
7859         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7860           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7861           if(elem->IsQuadratic()) {
7862             const SMDS_VtkFace* F =
7863               dynamic_cast<const SMDS_VtkFace*>(elem);
7864             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7865             // use special nodes iterator
7866             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7867             while( anIter->more() ) {
7868               nodes[ iNode ] = cast2Node(anIter->next());
7869               if ( nodes[ iNode++ ] == prevSideNode )
7870                 iPrevNode = iNode - 1;
7871             }
7872           }
7873           else {
7874             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7875             while ( nIt->more() ) {
7876               nodes[ iNode ] = cast2Node( nIt->next() );
7877               if ( nodes[ iNode++ ] == prevSideNode )
7878                 iPrevNode = iNode - 1;
7879             }
7880           }
7881           // there are 2 links to check
7882           nbNodes = 2;
7883         }
7884         else // --edge
7885           continue;
7886         // loop on links, to be precise, on the second node of links
7887         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7888           const SMDS_MeshNode* n = nodes[ iNode ];
7889           if ( isVolume ) {
7890             if ( !volume.IsLinked( n, prevSideNode ))
7891               continue;
7892           }
7893           else {
7894             if ( iNode ) // a node before prevSideNode
7895               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7896             else         // a node after prevSideNode
7897               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7898           }
7899           // check if this link was already used
7900           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7901           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7902           if (!isJustChecked &&
7903               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7904           {
7905             // test a link geometrically
7906             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7907             bool linkIsBetter = false;
7908             double dot = 0.0, dist = 0.0;
7909             if ( searchByDir ) { // choose most co-directed link
7910               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7911               linkIsBetter = ( dot > maxDot );
7912             }
7913             else { // choose link with the node closest to bordPos
7914               dist = ( nextXYZ - bordPos ).SquareModulus();
7915               linkIsBetter = ( dist < minDist );
7916             }
7917             if ( linkIsBetter ) {
7918               maxDot = dot;
7919               minDist = dist;
7920               linkID = iLink;
7921               sideNode = n;
7922               sideElem = elem;
7923             }
7924           }
7925         }
7926       } // loop on inverse elements of prevSideNode
7927
7928       if ( !sideNode ) {
7929         MESSAGE(" Cant find path by links of the Side 2 ");
7930         return SEW_BAD_SIDE_NODES;
7931       }
7932       sideNodes.push_back( sideNode );
7933       sideElems.push_back( sideElem );
7934       foundSideLinkIDs.insert ( linkID );
7935       prevSideNode = sideNode;
7936
7937       if ( *nBordIt == theBordLastNode )
7938         searchByDir = false;
7939       else {
7940         // find the next border link to compare with
7941         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7942         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7943         // move to next border node if sideNode is before forward border node (bordPos)
7944         while ( *nBordIt != theBordLastNode && !searchByDir ) {
7945           prevBordNode = *nBordIt;
7946           nBordIt++;
7947           bordPos = nBordXYZ[ *nBordIt ];
7948           bordDir = bordPos - nBordXYZ[ prevBordNode ];
7949           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7950         }
7951       }
7952     }
7953     while ( sideNode != theSideSecondNode );
7954
7955     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7956       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7957       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7958     }
7959   } // end nodes search on the side 2
7960
7961   // ============================
7962   // sew the border to the side 2
7963   // ============================
7964
7965   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
7966   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7967
7968   TListOfListOfNodes nodeGroupsToMerge;
7969   if ( nbNodes[0] == nbNodes[1] ||
7970        ( theSideIsFreeBorder && !theSideThirdNode)) {
7971
7972     // all nodes are to be merged
7973
7974     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7975          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7976          nIt[0]++, nIt[1]++ )
7977     {
7978       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7979       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7980       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7981     }
7982   }
7983   else {
7984
7985     // insert new nodes into the border and the side to get equal nb of segments
7986
7987     // get normalized parameters of nodes on the borders
7988     //double param[ 2 ][ maxNbNodes ];
7989     double* param[ 2 ];
7990     param[0] = new double [ maxNbNodes ];
7991     param[1] = new double [ maxNbNodes ];
7992     int iNode, iBord;
7993     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7994       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7995       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7996       const SMDS_MeshNode* nPrev = *nIt;
7997       double bordLength = 0;
7998       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7999         const SMDS_MeshNode* nCur = *nIt;
8000         gp_XYZ segment (nCur->X() - nPrev->X(),
8001                         nCur->Y() - nPrev->Y(),
8002                         nCur->Z() - nPrev->Z());
8003         double segmentLen = segment.Modulus();
8004         bordLength += segmentLen;
8005         param[ iBord ][ iNode ] = bordLength;
8006         nPrev = nCur;
8007       }
8008       // normalize within [0,1]
8009       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8010         param[ iBord ][ iNode ] /= bordLength;
8011       }
8012     }
8013
8014     // loop on border segments
8015     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8016     int i[ 2 ] = { 0, 0 };
8017     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8018     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8019
8020     TElemOfNodeListMap insertMap;
8021     TElemOfNodeListMap::iterator insertMapIt;
8022     // insertMap is
8023     // key:   elem to insert nodes into
8024     // value: 2 nodes to insert between + nodes to be inserted
8025     do {
8026       bool next[ 2 ] = { false, false };
8027
8028       // find min adjacent segment length after sewing
8029       double nextParam = 10., prevParam = 0;
8030       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8031         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8032           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8033         if ( i[ iBord ] > 0 )
8034           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8035       }
8036       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8037       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8038       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8039
8040       // choose to insert or to merge nodes
8041       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8042       if ( Abs( du ) <= minSegLen * 0.2 ) {
8043         // merge
8044         // ------
8045         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8046         const SMDS_MeshNode* n0 = *nIt[0];
8047         const SMDS_MeshNode* n1 = *nIt[1];
8048         nodeGroupsToMerge.back().push_back( n1 );
8049         nodeGroupsToMerge.back().push_back( n0 );
8050         // position of node of the border changes due to merge
8051         param[ 0 ][ i[0] ] += du;
8052         // move n1 for the sake of elem shape evaluation during insertion.
8053         // n1 will be removed by MergeNodes() anyway
8054         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8055         next[0] = next[1] = true;
8056       }
8057       else {
8058         // insert
8059         // ------
8060         int intoBord = ( du < 0 ) ? 0 : 1;
8061         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8062         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8063         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8064         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8065         if ( intoBord == 1 ) {
8066           // move node of the border to be on a link of elem of the side
8067           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8068           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8069           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8070           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8071           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8072         }
8073         insertMapIt = insertMap.find( elem );
8074         bool notFound = ( insertMapIt == insertMap.end() );
8075         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8076         if ( otherLink ) {
8077           // insert into another link of the same element:
8078           // 1. perform insertion into the other link of the elem
8079           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8080           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8081           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8082           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8083           // 2. perform insertion into the link of adjacent faces
8084           while (true) {
8085             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8086             if ( adjElem )
8087               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8088             else
8089               break;
8090           }
8091           if (toCreatePolyedrs) {
8092             // perform insertion into the links of adjacent volumes
8093             UpdateVolumes(n12, n22, nodeList);
8094           }
8095           // 3. find an element appeared on n1 and n2 after the insertion
8096           insertMap.erase( elem );
8097           elem = findAdjacentFace( n1, n2, 0 );
8098         }
8099         if ( notFound || otherLink ) {
8100           // add element and nodes of the side into the insertMap
8101           insertMapIt = insertMap.insert
8102             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8103           (*insertMapIt).second.push_back( n1 );
8104           (*insertMapIt).second.push_back( n2 );
8105         }
8106         // add node to be inserted into elem
8107         (*insertMapIt).second.push_back( nIns );
8108         next[ 1 - intoBord ] = true;
8109       }
8110
8111       // go to the next segment
8112       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8113         if ( next[ iBord ] ) {
8114           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8115             eIt[ iBord ]++;
8116           nPrev[ iBord ] = *nIt[ iBord ];
8117           nIt[ iBord ]++; i[ iBord ]++;
8118         }
8119       }
8120     }
8121     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8122
8123     // perform insertion of nodes into elements
8124
8125     for (insertMapIt = insertMap.begin();
8126          insertMapIt != insertMap.end();
8127          insertMapIt++ )
8128     {
8129       const SMDS_MeshElement* elem = (*insertMapIt).first;
8130       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8131       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8132       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8133
8134       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8135
8136       if ( !theSideIsFreeBorder ) {
8137         // look for and insert nodes into the faces adjacent to elem
8138         while (true) {
8139           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8140           if ( adjElem )
8141             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8142           else
8143             break;
8144         }
8145       }
8146       if (toCreatePolyedrs) {
8147         // perform insertion into the links of adjacent volumes
8148         UpdateVolumes(n1, n2, nodeList);
8149       }
8150     }
8151
8152     delete param[0];
8153     delete param[1];
8154   } // end: insert new nodes
8155
8156   MergeNodes ( nodeGroupsToMerge );
8157
8158   return aResult;
8159 }
8160
8161 //=======================================================================
8162 //function : InsertNodesIntoLink
8163 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8164 //           and theBetweenNode2 and split theElement
8165 //=======================================================================
8166
8167 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8168                                            const SMDS_MeshNode*        theBetweenNode1,
8169                                            const SMDS_MeshNode*        theBetweenNode2,
8170                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8171                                            const bool                  toCreatePoly)
8172 {
8173   if ( theFace->GetType() != SMDSAbs_Face ) return;
8174
8175   // find indices of 2 link nodes and of the rest nodes
8176   int iNode = 0, il1, il2, i3, i4;
8177   il1 = il2 = i3 = i4 = -1;
8178   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8179   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8180
8181   if(theFace->IsQuadratic()) {
8182     const SMDS_VtkFace* F =
8183       dynamic_cast<const SMDS_VtkFace*>(theFace);
8184     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8185     // use special nodes iterator
8186     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8187     while( anIter->more() ) {
8188       const SMDS_MeshNode* n = cast2Node(anIter->next());
8189       if ( n == theBetweenNode1 )
8190         il1 = iNode;
8191       else if ( n == theBetweenNode2 )
8192         il2 = iNode;
8193       else if ( i3 < 0 )
8194         i3 = iNode;
8195       else
8196         i4 = iNode;
8197       nodes[ iNode++ ] = n;
8198     }
8199   }
8200   else {
8201     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8202     while ( nodeIt->more() ) {
8203       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8204       if ( n == theBetweenNode1 )
8205         il1 = iNode;
8206       else if ( n == theBetweenNode2 )
8207         il2 = iNode;
8208       else if ( i3 < 0 )
8209         i3 = iNode;
8210       else
8211         i4 = iNode;
8212       nodes[ iNode++ ] = n;
8213     }
8214   }
8215   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8216     return ;
8217
8218   // arrange link nodes to go one after another regarding the face orientation
8219   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8220   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8221   if ( reverse ) {
8222     iNode = il1;
8223     il1 = il2;
8224     il2 = iNode;
8225     aNodesToInsert.reverse();
8226   }
8227   // check that not link nodes of a quadrangles are in good order
8228   int nbFaceNodes = theFace->NbNodes();
8229   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8230     iNode = i3;
8231     i3 = i4;
8232     i4 = iNode;
8233   }
8234
8235   if (toCreatePoly || theFace->IsPoly()) {
8236
8237     iNode = 0;
8238     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8239
8240     // add nodes of face up to first node of link
8241     bool isFLN = false;
8242
8243     if(theFace->IsQuadratic()) {
8244       const SMDS_VtkFace* F =
8245         dynamic_cast<const SMDS_VtkFace*>(theFace);
8246       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8247       // use special nodes iterator
8248       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8249       while( anIter->more()  && !isFLN ) {
8250         const SMDS_MeshNode* n = cast2Node(anIter->next());
8251         poly_nodes[iNode++] = n;
8252         if (n == nodes[il1]) {
8253           isFLN = true;
8254         }
8255       }
8256       // add nodes to insert
8257       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8258       for (; nIt != aNodesToInsert.end(); nIt++) {
8259         poly_nodes[iNode++] = *nIt;
8260       }
8261       // add nodes of face starting from last node of link
8262       while ( anIter->more() ) {
8263         poly_nodes[iNode++] = cast2Node(anIter->next());
8264       }
8265     }
8266     else {
8267       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8268       while ( nodeIt->more() && !isFLN ) {
8269         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8270         poly_nodes[iNode++] = n;
8271         if (n == nodes[il1]) {
8272           isFLN = true;
8273         }
8274       }
8275       // add nodes to insert
8276       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8277       for (; nIt != aNodesToInsert.end(); nIt++) {
8278         poly_nodes[iNode++] = *nIt;
8279       }
8280       // add nodes of face starting from last node of link
8281       while ( nodeIt->more() ) {
8282         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8283         poly_nodes[iNode++] = n;
8284       }
8285     }
8286
8287     // edit or replace the face
8288     SMESHDS_Mesh *aMesh = GetMeshDS();
8289
8290     if (theFace->IsPoly()) {
8291       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8292     }
8293     else {
8294       int aShapeId = FindShape( theFace );
8295
8296       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8297       myLastCreatedElems.Append(newElem);
8298       if ( aShapeId && newElem )
8299         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8300
8301       aMesh->RemoveElement(theFace);
8302     }
8303     return;
8304   }
8305
8306   SMESHDS_Mesh *aMesh = GetMeshDS();
8307   if( !theFace->IsQuadratic() ) {
8308
8309     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8310     int nbLinkNodes = 2 + aNodesToInsert.size();
8311     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8312     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8313     linkNodes[ 0 ] = nodes[ il1 ];
8314     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8315     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8316     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8317       linkNodes[ iNode++ ] = *nIt;
8318     }
8319     // decide how to split a quadrangle: compare possible variants
8320     // and choose which of splits to be a quadrangle
8321     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8322     if ( nbFaceNodes == 3 ) {
8323       iBestQuad = nbSplits;
8324       i4 = i3;
8325     }
8326     else if ( nbFaceNodes == 4 ) {
8327       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8328       double aBestRate = DBL_MAX;
8329       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8330         i1 = 0; i2 = 1;
8331         double aBadRate = 0;
8332         // evaluate elements quality
8333         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8334           if ( iSplit == iQuad ) {
8335             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8336                                    linkNodes[ i2++ ],
8337                                    nodes[ i3 ],
8338                                    nodes[ i4 ]);
8339             aBadRate += getBadRate( &quad, aCrit );
8340           }
8341           else {
8342             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8343                                    linkNodes[ i2++ ],
8344                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8345             aBadRate += getBadRate( &tria, aCrit );
8346           }
8347         }
8348         // choice
8349         if ( aBadRate < aBestRate ) {
8350           iBestQuad = iQuad;
8351           aBestRate = aBadRate;
8352         }
8353       }
8354     }
8355
8356     // create new elements
8357     int aShapeId = FindShape( theFace );
8358
8359     i1 = 0; i2 = 1;
8360     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8361       SMDS_MeshElement* newElem = 0;
8362       if ( iSplit == iBestQuad )
8363         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8364                                   linkNodes[ i2++ ],
8365                                   nodes[ i3 ],
8366                                   nodes[ i4 ]);
8367       else
8368         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8369                                   linkNodes[ i2++ ],
8370                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8371       myLastCreatedElems.Append(newElem);
8372       if ( aShapeId && newElem )
8373         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8374     }
8375
8376     // change nodes of theFace
8377     const SMDS_MeshNode* newNodes[ 4 ];
8378     newNodes[ 0 ] = linkNodes[ i1 ];
8379     newNodes[ 1 ] = linkNodes[ i2 ];
8380     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8381     newNodes[ 3 ] = nodes[ i4 ];
8382     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8383     const SMDS_MeshElement* newElem = 0;
8384     if (iSplit == iBestQuad)
8385       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8386     else
8387       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8388     myLastCreatedElems.Append(newElem);
8389     if ( aShapeId && newElem )
8390       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8391 } // end if(!theFace->IsQuadratic())
8392   else { // theFace is quadratic
8393     // we have to split theFace on simple triangles and one simple quadrangle
8394     int tmp = il1/2;
8395     int nbshift = tmp*2;
8396     // shift nodes in nodes[] by nbshift
8397     int i,j;
8398     for(i=0; i<nbshift; i++) {
8399       const SMDS_MeshNode* n = nodes[0];
8400       for(j=0; j<nbFaceNodes-1; j++) {
8401         nodes[j] = nodes[j+1];
8402       }
8403       nodes[nbFaceNodes-1] = n;
8404     }
8405     il1 = il1 - nbshift;
8406     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8407     //   n0      n1     n2    n0      n1     n2
8408     //     +-----+-----+        +-----+-----+
8409     //      \         /         |           |
8410     //       \       /          |           |
8411     //      n5+     +n3       n7+           +n3
8412     //         \   /            |           |
8413     //          \ /             |           |
8414     //           +              +-----+-----+
8415     //           n4           n6      n5     n4
8416
8417     // create new elements
8418     int aShapeId = FindShape( theFace );
8419
8420     int n1,n2,n3;
8421     if(nbFaceNodes==6) { // quadratic triangle
8422       SMDS_MeshElement* newElem =
8423         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8424       myLastCreatedElems.Append(newElem);
8425       if ( aShapeId && newElem )
8426         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8427       if(theFace->IsMediumNode(nodes[il1])) {
8428         // create quadrangle
8429         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8430         myLastCreatedElems.Append(newElem);
8431         if ( aShapeId && newElem )
8432           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8433         n1 = 1;
8434         n2 = 2;
8435         n3 = 3;
8436       }
8437       else {
8438         // create quadrangle
8439         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8440         myLastCreatedElems.Append(newElem);
8441         if ( aShapeId && newElem )
8442           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8443         n1 = 0;
8444         n2 = 1;
8445         n3 = 5;
8446       }
8447     }
8448     else { // nbFaceNodes==8 - quadratic quadrangle
8449       SMDS_MeshElement* newElem =
8450         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8451       myLastCreatedElems.Append(newElem);
8452       if ( aShapeId && newElem )
8453         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8454       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8455       myLastCreatedElems.Append(newElem);
8456       if ( aShapeId && newElem )
8457         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8458       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8459       myLastCreatedElems.Append(newElem);
8460       if ( aShapeId && newElem )
8461         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8462       if(theFace->IsMediumNode(nodes[il1])) {
8463         // create quadrangle
8464         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8465         myLastCreatedElems.Append(newElem);
8466         if ( aShapeId && newElem )
8467           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8468         n1 = 1;
8469         n2 = 2;
8470         n3 = 3;
8471       }
8472       else {
8473         // create quadrangle
8474         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8475         myLastCreatedElems.Append(newElem);
8476         if ( aShapeId && newElem )
8477           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8478         n1 = 0;
8479         n2 = 1;
8480         n3 = 7;
8481       }
8482     }
8483     // create needed triangles using n1,n2,n3 and inserted nodes
8484     int nbn = 2 + aNodesToInsert.size();
8485     //const SMDS_MeshNode* aNodes[nbn];
8486     vector<const SMDS_MeshNode*> aNodes(nbn);
8487     aNodes[0] = nodes[n1];
8488     aNodes[nbn-1] = nodes[n2];
8489     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8490     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8491       aNodes[iNode++] = *nIt;
8492     }
8493     for(i=1; i<nbn; i++) {
8494       SMDS_MeshElement* newElem =
8495         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8496       myLastCreatedElems.Append(newElem);
8497       if ( aShapeId && newElem )
8498         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8499     }
8500   }
8501   // remove old face
8502   aMesh->RemoveElement(theFace);
8503 }
8504
8505 //=======================================================================
8506 //function : UpdateVolumes
8507 //purpose  :
8508 //=======================================================================
8509 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8510                                       const SMDS_MeshNode*        theBetweenNode2,
8511                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8512 {
8513   myLastCreatedElems.Clear();
8514   myLastCreatedNodes.Clear();
8515
8516   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8517   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8518     const SMDS_MeshElement* elem = invElemIt->next();
8519
8520     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8521     SMDS_VolumeTool aVolume (elem);
8522     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8523       continue;
8524
8525     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8526     int iface, nbFaces = aVolume.NbFaces();
8527     vector<const SMDS_MeshNode *> poly_nodes;
8528     vector<int> quantities (nbFaces);
8529
8530     for (iface = 0; iface < nbFaces; iface++) {
8531       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8532       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8533       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8534
8535       for (int inode = 0; inode < nbFaceNodes; inode++) {
8536         poly_nodes.push_back(faceNodes[inode]);
8537
8538         if (nbInserted == 0) {
8539           if (faceNodes[inode] == theBetweenNode1) {
8540             if (faceNodes[inode + 1] == theBetweenNode2) {
8541               nbInserted = theNodesToInsert.size();
8542
8543               // add nodes to insert
8544               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8545               for (; nIt != theNodesToInsert.end(); nIt++) {
8546                 poly_nodes.push_back(*nIt);
8547               }
8548             }
8549           }
8550           else if (faceNodes[inode] == theBetweenNode2) {
8551             if (faceNodes[inode + 1] == theBetweenNode1) {
8552               nbInserted = theNodesToInsert.size();
8553
8554               // add nodes to insert in reversed order
8555               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8556               nIt--;
8557               for (; nIt != theNodesToInsert.begin(); nIt--) {
8558                 poly_nodes.push_back(*nIt);
8559               }
8560               poly_nodes.push_back(*nIt);
8561             }
8562           }
8563           else {
8564           }
8565         }
8566       }
8567       quantities[iface] = nbFaceNodes + nbInserted;
8568     }
8569
8570     // Replace or update the volume
8571     SMESHDS_Mesh *aMesh = GetMeshDS();
8572
8573     if (elem->IsPoly()) {
8574       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8575
8576     }
8577     else {
8578       int aShapeId = FindShape( elem );
8579
8580       SMDS_MeshElement* newElem =
8581         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8582       myLastCreatedElems.Append(newElem);
8583       if (aShapeId && newElem)
8584         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8585
8586       aMesh->RemoveElement(elem);
8587     }
8588   }
8589 }
8590
8591 namespace
8592 {
8593   //================================================================================
8594   /*!
8595    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8596    */
8597   //================================================================================
8598
8599   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8600                            vector<const SMDS_MeshNode *> & nodes,
8601                            vector<int> &                   nbNodeInFaces )
8602   {
8603     nodes.clear();
8604     nbNodeInFaces.clear();
8605     SMDS_VolumeTool vTool ( elem );
8606     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8607     {
8608       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8609       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8610       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8611     }
8612   }
8613 }
8614
8615 //=======================================================================
8616 /*!
8617  * \brief Convert elements contained in a submesh to quadratic
8618  * \return int - nb of checked elements
8619  */
8620 //=======================================================================
8621
8622 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8623                                              SMESH_MesherHelper& theHelper,
8624                                              const bool          theForce3d)
8625 {
8626   int nbElem = 0;
8627   if( !theSm ) return nbElem;
8628
8629   vector<int> nbNodeInFaces;
8630   vector<const SMDS_MeshNode *> nodes;
8631   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8632   while(ElemItr->more())
8633   {
8634     nbElem++;
8635     const SMDS_MeshElement* elem = ElemItr->next();
8636     if( !elem ) continue;
8637
8638     // analyse a necessity of conversion
8639     const SMDSAbs_ElementType aType = elem->GetType();
8640     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8641       continue;
8642     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8643     bool hasCentralNodes = false;
8644     if ( elem->IsQuadratic() )
8645     {
8646       bool alreadyOK;
8647       switch ( aGeomType ) {
8648       case SMDSEntity_Quad_Triangle:
8649       case SMDSEntity_Quad_Quadrangle:
8650       case SMDSEntity_Quad_Hexa:
8651         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8652
8653       case SMDSEntity_BiQuad_Triangle:
8654       case SMDSEntity_BiQuad_Quadrangle:
8655       case SMDSEntity_TriQuad_Hexa:
8656         alreadyOK = theHelper.GetIsBiQuadratic();
8657         hasCentralNodes = true;
8658         break;
8659       default:
8660         alreadyOK = true;
8661       }
8662       // take into account already present modium nodes
8663       switch ( aType ) {
8664       case SMDSAbs_Volume:
8665         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8666       case SMDSAbs_Face:
8667         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8668       case SMDSAbs_Edge:
8669         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8670       default:;
8671       }
8672       if ( alreadyOK )
8673         continue;
8674     }
8675     // get elem data needed to re-create it
8676     //
8677     const int id      = elem->GetID();
8678     const int nbNodes = elem->NbCornerNodes();
8679     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8680     if ( aGeomType == SMDSEntity_Polyhedra )
8681       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8682     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8683       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8684
8685     // remove a linear element
8686     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8687
8688     // remove central nodes of biquadratic elements (biquad->quad convertion)
8689     if ( hasCentralNodes )
8690       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8691         if ( nodes[i]->NbInverseElements() == 0 )
8692           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8693
8694     const SMDS_MeshElement* NewElem = 0;
8695
8696     switch( aType )
8697     {
8698     case SMDSAbs_Edge :
8699       {
8700         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8701         break;
8702       }
8703     case SMDSAbs_Face :
8704       {
8705         switch(nbNodes)
8706         {
8707         case 3:
8708           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8709           break;
8710         case 4:
8711           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8712           break;
8713         default:
8714           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8715         }
8716         break;
8717       }
8718     case SMDSAbs_Volume :
8719       {
8720         switch( aGeomType )
8721         {
8722         case SMDSEntity_Tetra:
8723           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8724           break;
8725         case SMDSEntity_Pyramid:
8726           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8727           break;
8728         case SMDSEntity_Penta:
8729           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8730           break;
8731         case SMDSEntity_Hexa:
8732         case SMDSEntity_Quad_Hexa:
8733         case SMDSEntity_TriQuad_Hexa:
8734           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8735                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8736           break;
8737         case SMDSEntity_Hexagonal_Prism:
8738         default:
8739           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8740         }
8741         break;
8742       }
8743     default :
8744       continue;
8745     }
8746     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8747     if( NewElem && NewElem->getshapeId() < 1 )
8748       theSm->AddElement( NewElem );
8749   }
8750   return nbElem;
8751 }
8752 //=======================================================================
8753 //function : ConvertToQuadratic
8754 //purpose  :
8755 //=======================================================================
8756
8757 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8758 {
8759   SMESHDS_Mesh* meshDS = GetMeshDS();
8760
8761   SMESH_MesherHelper aHelper(*myMesh);
8762
8763   aHelper.SetIsQuadratic( true );
8764   aHelper.SetIsBiQuadratic( theToBiQuad );
8765   aHelper.SetElementsOnShape(true);
8766   aHelper.ToFixNodeParameters( true );
8767
8768   // convert elements assigned to sub-meshes
8769   int nbCheckedElems = 0;
8770   if ( myMesh->HasShapeToMesh() )
8771   {
8772     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8773     {
8774       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8775       while ( smIt->more() ) {
8776         SMESH_subMesh* sm = smIt->next();
8777         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8778           aHelper.SetSubShape( sm->GetSubShape() );
8779           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8780         }
8781       }
8782     }
8783   }
8784
8785   // convert elements NOT assigned to sub-meshes
8786   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8787   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8788   {
8789     aHelper.SetElementsOnShape(false);
8790     SMESHDS_SubMesh *smDS = 0;
8791
8792     // convert edges
8793     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8794     while( aEdgeItr->more() )
8795     {
8796       const SMDS_MeshEdge* edge = aEdgeItr->next();
8797       if ( !edge->IsQuadratic() )
8798       {
8799         int                  id = edge->GetID();
8800         const SMDS_MeshNode* n1 = edge->GetNode(0);
8801         const SMDS_MeshNode* n2 = edge->GetNode(1);
8802
8803         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8804
8805         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8806         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8807       }
8808       else
8809       {
8810         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8811       }
8812     }
8813
8814     // convert faces
8815     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8816     while( aFaceItr->more() )
8817     {
8818       const SMDS_MeshFace* face = aFaceItr->next();
8819       if ( !face ) continue;
8820       
8821       const SMDSAbs_EntityType type = face->GetEntityType();
8822       bool alreadyOK;
8823       switch( type )
8824       {
8825       case SMDSEntity_Quad_Triangle:
8826       case SMDSEntity_Quad_Quadrangle:
8827         alreadyOK = !theToBiQuad;
8828         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8829         break;
8830       case SMDSEntity_BiQuad_Triangle:
8831       case SMDSEntity_BiQuad_Quadrangle:
8832         alreadyOK = theToBiQuad;
8833         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8834         break;
8835       default: alreadyOK = false;
8836       }
8837       if ( alreadyOK )
8838         continue;
8839
8840       const int id = face->GetID();
8841       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8842
8843       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8844
8845       SMDS_MeshFace * NewFace = 0;
8846       switch( type )
8847       {
8848       case SMDSEntity_Triangle:
8849       case SMDSEntity_Quad_Triangle:
8850       case SMDSEntity_BiQuad_Triangle:
8851         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8852         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8853           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8854         break;
8855
8856       case SMDSEntity_Quadrangle:
8857       case SMDSEntity_Quad_Quadrangle:
8858       case SMDSEntity_BiQuad_Quadrangle:
8859         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8860         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8861           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8862         break;
8863
8864       default:;
8865         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8866       }
8867       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8868     }
8869
8870     // convert volumes
8871     vector<int> nbNodeInFaces;
8872     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8873     while(aVolumeItr->more())
8874     {
8875       const SMDS_MeshVolume* volume = aVolumeItr->next();
8876       if ( !volume ) continue;
8877
8878       const SMDSAbs_EntityType type = volume->GetEntityType();
8879       if ( volume->IsQuadratic() )
8880       {
8881         bool alreadyOK;
8882         switch ( type )
8883         {
8884         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8885         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8886         default:                      alreadyOK = true;
8887         }
8888         if ( alreadyOK )
8889         {
8890           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8891           continue;
8892         }
8893       }
8894       const int id = volume->GetID();
8895       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8896       if ( type == SMDSEntity_Polyhedra )
8897         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8898       else if ( type == SMDSEntity_Hexagonal_Prism )
8899         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8900
8901       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8902
8903       SMDS_MeshVolume * NewVolume = 0;
8904       switch ( type )
8905       {
8906       case SMDSEntity_Tetra:
8907         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8908         break;
8909       case SMDSEntity_Hexa:
8910       case SMDSEntity_Quad_Hexa:
8911       case SMDSEntity_TriQuad_Hexa:
8912         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8913                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8914         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8915           if ( nodes[i]->NbInverseElements() == 0 )
8916             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8917         break;
8918       case SMDSEntity_Pyramid:
8919         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8920                                       nodes[3], nodes[4], id, theForce3d);
8921         break;
8922       case SMDSEntity_Penta:
8923         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8924                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
8925         break;
8926       case SMDSEntity_Hexagonal_Prism:
8927       default:
8928         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8929       }
8930       ReplaceElemInGroups(volume, NewVolume, meshDS);
8931     }
8932   }
8933
8934   if ( !theForce3d )
8935   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8936     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8937     // aHelper.FixQuadraticElements(myError);
8938     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8939   }
8940 }
8941
8942 //================================================================================
8943 /*!
8944  * \brief Makes given elements quadratic
8945  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
8946  *  \param theElements - elements to make quadratic
8947  */
8948 //================================================================================
8949
8950 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
8951                                           TIDSortedElemSet& theElements,
8952                                           const bool        theToBiQuad)
8953 {
8954   if ( theElements.empty() ) return;
8955
8956   // we believe that all theElements are of the same type
8957   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8958
8959   // get all nodes shared by theElements
8960   TIDSortedNodeSet allNodes;
8961   TIDSortedElemSet::iterator eIt = theElements.begin();
8962   for ( ; eIt != theElements.end(); ++eIt )
8963     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8964
8965   // complete theElements with elements of lower dim whose all nodes are in allNodes
8966
8967   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8968   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8969   TIDSortedNodeSet::iterator nIt = allNodes.begin();
8970   for ( ; nIt != allNodes.end(); ++nIt )
8971   {
8972     const SMDS_MeshNode* n = *nIt;
8973     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8974     while ( invIt->more() )
8975     {
8976       const SMDS_MeshElement*      e = invIt->next();
8977       const SMDSAbs_ElementType type = e->GetType();
8978       if ( e->IsQuadratic() )
8979       {
8980         quadAdjacentElems[ type ].insert( e );
8981
8982         bool alreadyOK;
8983         switch ( e->GetEntityType() ) {
8984         case SMDSEntity_Quad_Triangle:
8985         case SMDSEntity_Quad_Quadrangle:
8986         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
8987         case SMDSEntity_BiQuad_Triangle:
8988         case SMDSEntity_BiQuad_Quadrangle:
8989         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
8990         default:                           alreadyOK = true;
8991         }
8992         if ( alreadyOK )
8993           continue;
8994       }
8995       if ( type >= elemType )
8996         continue; // same type or more complex linear element
8997
8998       if ( !checkedAdjacentElems[ type ].insert( e ).second )
8999         continue; // e is already checked
9000
9001       // check nodes
9002       bool allIn = true;
9003       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9004       while ( nodeIt->more() && allIn )
9005         allIn = allNodes.count( nodeIt->next() );
9006       if ( allIn )
9007         theElements.insert(e );
9008     }
9009   }
9010
9011   SMESH_MesherHelper helper(*myMesh);
9012   helper.SetIsQuadratic( true );
9013   helper.SetIsBiQuadratic( theToBiQuad );
9014
9015   // add links of quadratic adjacent elements to the helper
9016
9017   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9018     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9019           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9020     {
9021       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9022     }
9023   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9024     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9025           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9026     {
9027       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9028     }
9029   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9030     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9031           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9032     {
9033       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9034     }
9035
9036   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9037
9038   SMESHDS_Mesh*  meshDS = GetMeshDS();
9039   SMESHDS_SubMesh* smDS = 0;
9040   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9041   {
9042     const SMDS_MeshElement* elem = *eIt;
9043
9044     bool alreadyOK;
9045     int nbCentralNodes = 0;
9046     switch ( elem->GetEntityType() ) {
9047       // linear convertible
9048     case SMDSEntity_Edge:
9049     case SMDSEntity_Triangle:
9050     case SMDSEntity_Quadrangle:
9051     case SMDSEntity_Tetra:
9052     case SMDSEntity_Pyramid:
9053     case SMDSEntity_Hexa:
9054     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9055       // quadratic that can become bi-quadratic
9056     case SMDSEntity_Quad_Triangle:
9057     case SMDSEntity_Quad_Quadrangle:
9058     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9059       // bi-quadratic
9060     case SMDSEntity_BiQuad_Triangle:
9061     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9062     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9063       // the rest
9064     default:                           alreadyOK = true;
9065     }
9066     if ( alreadyOK ) continue;
9067
9068     const SMDSAbs_ElementType type = elem->GetType();
9069     const int                   id = elem->GetID();
9070     const int              nbNodes = elem->NbCornerNodes();
9071     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9072
9073     helper.SetSubShape( elem->getshapeId() );
9074
9075     if ( !smDS || !smDS->Contains( elem ))
9076       smDS = meshDS->MeshElements( elem->getshapeId() );
9077     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9078
9079     SMDS_MeshElement * newElem = 0;
9080     switch( nbNodes )
9081     {
9082     case 4: // cases for most frequently used element types go first (for optimization)
9083       if ( type == SMDSAbs_Volume )
9084         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9085       else
9086         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9087       break;
9088     case 8:
9089       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9090                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9091       break;
9092     case 3:
9093       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9094       break;
9095     case 2:
9096       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9097       break;
9098     case 5:
9099       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9100                                  nodes[4], id, theForce3d);
9101       break;
9102     case 6:
9103       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9104                                  nodes[4], nodes[5], id, theForce3d);
9105       break;
9106     default:;
9107     }
9108     ReplaceElemInGroups( elem, newElem, meshDS);
9109     if( newElem && smDS )
9110       smDS->AddElement( newElem );
9111
9112      // remove central nodes
9113     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9114       if ( nodes[i]->NbInverseElements() == 0 )
9115         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9116
9117   } // loop on theElements
9118
9119   if ( !theForce3d )
9120   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9121     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9122     // helper.FixQuadraticElements( myError );
9123     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9124   }
9125 }
9126
9127 //=======================================================================
9128 /*!
9129  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9130  * \return int - nb of checked elements
9131  */
9132 //=======================================================================
9133
9134 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9135                                      SMDS_ElemIteratorPtr theItr,
9136                                      const int            theShapeID)
9137 {
9138   int nbElem = 0;
9139   SMESHDS_Mesh* meshDS = GetMeshDS();
9140
9141   while( theItr->more() )
9142   {
9143     const SMDS_MeshElement* elem = theItr->next();
9144     nbElem++;
9145     if( elem && elem->IsQuadratic())
9146     {
9147       int id                    = elem->GetID();
9148       int nbCornerNodes         = elem->NbCornerNodes();
9149       SMDSAbs_ElementType aType = elem->GetType();
9150
9151       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9152
9153       //remove a quadratic element
9154       if ( !theSm || !theSm->Contains( elem ))
9155         theSm = meshDS->MeshElements( elem->getshapeId() );
9156       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9157
9158       // remove medium nodes
9159       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9160         if ( nodes[i]->NbInverseElements() == 0 )
9161           meshDS->RemoveFreeNode( nodes[i], theSm );
9162
9163       // add a linear element
9164       nodes.resize( nbCornerNodes );
9165       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9166       ReplaceElemInGroups(elem, newElem, meshDS);
9167       if( theSm && newElem )
9168         theSm->AddElement( newElem );
9169     }
9170   }
9171   return nbElem;
9172 }
9173
9174 //=======================================================================
9175 //function : ConvertFromQuadratic
9176 //purpose  :
9177 //=======================================================================
9178
9179 bool SMESH_MeshEditor::ConvertFromQuadratic()
9180 {
9181   int nbCheckedElems = 0;
9182   if ( myMesh->HasShapeToMesh() )
9183   {
9184     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9185     {
9186       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9187       while ( smIt->more() ) {
9188         SMESH_subMesh* sm = smIt->next();
9189         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9190           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9191       }
9192     }
9193   }
9194
9195   int totalNbElems =
9196     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9197   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9198   {
9199     SMESHDS_SubMesh *aSM = 0;
9200     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9201   }
9202
9203   return true;
9204 }
9205
9206 namespace
9207 {
9208   //================================================================================
9209   /*!
9210    * \brief Return true if all medium nodes of the element are in the node set
9211    */
9212   //================================================================================
9213
9214   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9215   {
9216     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9217       if ( !nodeSet.count( elem->GetNode(i) ))
9218         return false;
9219     return true;
9220   }
9221 }
9222
9223 //================================================================================
9224 /*!
9225  * \brief Makes given elements linear
9226  */
9227 //================================================================================
9228
9229 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9230 {
9231   if ( theElements.empty() ) return;
9232
9233   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9234   set<int> mediumNodeIDs;
9235   TIDSortedElemSet::iterator eIt = theElements.begin();
9236   for ( ; eIt != theElements.end(); ++eIt )
9237   {
9238     const SMDS_MeshElement* e = *eIt;
9239     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9240       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9241   }
9242
9243   // replace given elements by linear ones
9244   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9245   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9246
9247   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9248   // except those elements sharing medium nodes of quadratic element whose medium nodes
9249   // are not all in mediumNodeIDs
9250
9251   // get remaining medium nodes
9252   TIDSortedNodeSet mediumNodes;
9253   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9254   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9255     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9256       mediumNodes.insert( mediumNodes.end(), n );
9257
9258   // find more quadratic elements to convert
9259   TIDSortedElemSet moreElemsToConvert;
9260   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9261   for ( ; nIt != mediumNodes.end(); ++nIt )
9262   {
9263     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9264     while ( invIt->more() )
9265     {
9266       const SMDS_MeshElement* e = invIt->next();
9267       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9268       {
9269         // find a more complex element including e and
9270         // whose medium nodes are not in mediumNodes
9271         bool complexFound = false;
9272         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9273         {
9274           SMDS_ElemIteratorPtr invIt2 =
9275             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9276           while ( invIt2->more() )
9277           {
9278             const SMDS_MeshElement* eComplex = invIt2->next();
9279             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9280             {
9281               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9282               if ( nbCommonNodes == e->NbNodes())
9283               {
9284                 complexFound = true;
9285                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9286                 break;
9287               }
9288             }
9289           }
9290         }
9291         if ( !complexFound )
9292           moreElemsToConvert.insert( e );
9293       }
9294     }
9295   }
9296   elemIt = elemSetIterator( moreElemsToConvert );
9297   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9298 }
9299
9300 //=======================================================================
9301 //function : SewSideElements
9302 //purpose  :
9303 //=======================================================================
9304
9305 SMESH_MeshEditor::Sew_Error
9306 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9307                                    TIDSortedElemSet&    theSide2,
9308                                    const SMDS_MeshNode* theFirstNode1,
9309                                    const SMDS_MeshNode* theFirstNode2,
9310                                    const SMDS_MeshNode* theSecondNode1,
9311                                    const SMDS_MeshNode* theSecondNode2)
9312 {
9313   myLastCreatedElems.Clear();
9314   myLastCreatedNodes.Clear();
9315
9316   MESSAGE ("::::SewSideElements()");
9317   if ( theSide1.size() != theSide2.size() )
9318     return SEW_DIFF_NB_OF_ELEMENTS;
9319
9320   Sew_Error aResult = SEW_OK;
9321   // Algo:
9322   // 1. Build set of faces representing each side
9323   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9324   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9325
9326   // =======================================================================
9327   // 1. Build set of faces representing each side:
9328   // =======================================================================
9329   // a. build set of nodes belonging to faces
9330   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9331   // c. create temporary faces representing side of volumes if correspondent
9332   //    face does not exist
9333
9334   SMESHDS_Mesh* aMesh = GetMeshDS();
9335   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9336   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9337   TIDSortedElemSet             faceSet1, faceSet2;
9338   set<const SMDS_MeshElement*> volSet1,  volSet2;
9339   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9340   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9341   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9342   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9343   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9344   int iSide, iFace, iNode;
9345
9346   list<const SMDS_MeshElement* > tempFaceList;
9347   for ( iSide = 0; iSide < 2; iSide++ ) {
9348     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9349     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9350     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9351     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9352     set<const SMDS_MeshElement*>::iterator vIt;
9353     TIDSortedElemSet::iterator eIt;
9354     set<const SMDS_MeshNode*>::iterator    nIt;
9355
9356     // check that given nodes belong to given elements
9357     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9358     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9359     int firstIndex = -1, secondIndex = -1;
9360     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9361       const SMDS_MeshElement* elem = *eIt;
9362       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9363       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9364       if ( firstIndex > -1 && secondIndex > -1 ) break;
9365     }
9366     if ( firstIndex < 0 || secondIndex < 0 ) {
9367       // we can simply return until temporary faces created
9368       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9369     }
9370
9371     // -----------------------------------------------------------
9372     // 1a. Collect nodes of existing faces
9373     //     and build set of face nodes in order to detect missing
9374     //     faces corresponding to sides of volumes
9375     // -----------------------------------------------------------
9376
9377     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9378
9379     // loop on the given element of a side
9380     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9381       //const SMDS_MeshElement* elem = *eIt;
9382       const SMDS_MeshElement* elem = *eIt;
9383       if ( elem->GetType() == SMDSAbs_Face ) {
9384         faceSet->insert( elem );
9385         set <const SMDS_MeshNode*> faceNodeSet;
9386         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9387         while ( nodeIt->more() ) {
9388           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9389           nodeSet->insert( n );
9390           faceNodeSet.insert( n );
9391         }
9392         setOfFaceNodeSet.insert( faceNodeSet );
9393       }
9394       else if ( elem->GetType() == SMDSAbs_Volume )
9395         volSet->insert( elem );
9396     }
9397     // ------------------------------------------------------------------------------
9398     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9399     // ------------------------------------------------------------------------------
9400
9401     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9402       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9403       while ( fIt->more() ) { // loop on faces sharing a node
9404         const SMDS_MeshElement* f = fIt->next();
9405         if ( faceSet->find( f ) == faceSet->end() ) {
9406           // check if all nodes are in nodeSet and
9407           // complete setOfFaceNodeSet if they are
9408           set <const SMDS_MeshNode*> faceNodeSet;
9409           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9410           bool allInSet = true;
9411           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9412             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9413             if ( nodeSet->find( n ) == nodeSet->end() )
9414               allInSet = false;
9415             else
9416               faceNodeSet.insert( n );
9417           }
9418           if ( allInSet ) {
9419             faceSet->insert( f );
9420             setOfFaceNodeSet.insert( faceNodeSet );
9421           }
9422         }
9423       }
9424     }
9425
9426     // -------------------------------------------------------------------------
9427     // 1c. Create temporary faces representing sides of volumes if correspondent
9428     //     face does not exist
9429     // -------------------------------------------------------------------------
9430
9431     if ( !volSet->empty() ) {
9432       //int nodeSetSize = nodeSet->size();
9433
9434       // loop on given volumes
9435       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9436         SMDS_VolumeTool vol (*vIt);
9437         // loop on volume faces: find free faces
9438         // --------------------------------------
9439         list<const SMDS_MeshElement* > freeFaceList;
9440         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9441           if ( !vol.IsFreeFace( iFace ))
9442             continue;
9443           // check if there is already a face with same nodes in a face set
9444           const SMDS_MeshElement* aFreeFace = 0;
9445           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9446           int nbNodes = vol.NbFaceNodes( iFace );
9447           set <const SMDS_MeshNode*> faceNodeSet;
9448           vol.GetFaceNodes( iFace, faceNodeSet );
9449           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9450           if ( isNewFace ) {
9451             // no such a face is given but it still can exist, check it
9452             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9453             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9454           }
9455           if ( !aFreeFace ) {
9456             // create a temporary face
9457             if ( nbNodes == 3 ) {
9458               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9459               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9460             }
9461             else if ( nbNodes == 4 ) {
9462               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9463               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9464             }
9465             else {
9466               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9467               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9468               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9469             }
9470             if ( aFreeFace )
9471               tempFaceList.push_back( aFreeFace );
9472           }
9473
9474           if ( aFreeFace )
9475             freeFaceList.push_back( aFreeFace );
9476
9477         } // loop on faces of a volume
9478
9479         // choose one of several free faces of a volume
9480         // --------------------------------------------
9481         if ( freeFaceList.size() > 1 ) {
9482           // choose a face having max nb of nodes shared by other elems of a side
9483           int maxNbNodes = -1;
9484           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9485           while ( fIt != freeFaceList.end() ) { // loop on free faces
9486             int nbSharedNodes = 0;
9487             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9488             while ( nodeIt->more() ) { // loop on free face nodes
9489               const SMDS_MeshNode* n =
9490                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9491               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9492               while ( invElemIt->more() ) {
9493                 const SMDS_MeshElement* e = invElemIt->next();
9494                 nbSharedNodes += faceSet->count( e );
9495                 nbSharedNodes += elemSet->count( e );
9496               }
9497             }
9498             if ( nbSharedNodes > maxNbNodes ) {
9499               maxNbNodes = nbSharedNodes;
9500               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9501             }
9502             else if ( nbSharedNodes == maxNbNodes ) {
9503               fIt++;
9504             }
9505             else {
9506               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9507             }
9508           }
9509           if ( freeFaceList.size() > 1 )
9510           {
9511             // could not choose one face, use another way
9512             // choose a face most close to the bary center of the opposite side
9513             gp_XYZ aBC( 0., 0., 0. );
9514             set <const SMDS_MeshNode*> addedNodes;
9515             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9516             eIt = elemSet2->begin();
9517             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9518               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9519               while ( nodeIt->more() ) { // loop on free face nodes
9520                 const SMDS_MeshNode* n =
9521                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9522                 if ( addedNodes.insert( n ).second )
9523                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9524               }
9525             }
9526             aBC /= addedNodes.size();
9527             double minDist = DBL_MAX;
9528             fIt = freeFaceList.begin();
9529             while ( fIt != freeFaceList.end() ) { // loop on free faces
9530               double dist = 0;
9531               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9532               while ( nodeIt->more() ) { // loop on free face nodes
9533                 const SMDS_MeshNode* n =
9534                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9535                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9536                 dist += ( aBC - p ).SquareModulus();
9537               }
9538               if ( dist < minDist ) {
9539                 minDist = dist;
9540                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9541               }
9542               else
9543                 fIt = freeFaceList.erase( fIt++ );
9544             }
9545           }
9546         } // choose one of several free faces of a volume
9547
9548         if ( freeFaceList.size() == 1 ) {
9549           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9550           faceSet->insert( aFreeFace );
9551           // complete a node set with nodes of a found free face
9552           //           for ( iNode = 0; iNode < ; iNode++ )
9553           //             nodeSet->insert( fNodes[ iNode ] );
9554         }
9555
9556       } // loop on volumes of a side
9557
9558       //       // complete a set of faces if new nodes in a nodeSet appeared
9559       //       // ----------------------------------------------------------
9560       //       if ( nodeSetSize != nodeSet->size() ) {
9561       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9562       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9563       //           while ( fIt->more() ) { // loop on faces sharing a node
9564       //             const SMDS_MeshElement* f = fIt->next();
9565       //             if ( faceSet->find( f ) == faceSet->end() ) {
9566       //               // check if all nodes are in nodeSet and
9567       //               // complete setOfFaceNodeSet if they are
9568       //               set <const SMDS_MeshNode*> faceNodeSet;
9569       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9570       //               bool allInSet = true;
9571       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9572       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9573       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9574       //                   allInSet = false;
9575       //                 else
9576       //                   faceNodeSet.insert( n );
9577       //               }
9578       //               if ( allInSet ) {
9579       //                 faceSet->insert( f );
9580       //                 setOfFaceNodeSet.insert( faceNodeSet );
9581       //               }
9582       //             }
9583       //           }
9584       //         }
9585       //       }
9586     } // Create temporary faces, if there are volumes given
9587   } // loop on sides
9588
9589   if ( faceSet1.size() != faceSet2.size() ) {
9590     // delete temporary faces: they are in reverseElements of actual nodes
9591 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9592 //    while ( tmpFaceIt->more() )
9593 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9594 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9595 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9596 //      aMesh->RemoveElement(*tmpFaceIt);
9597     MESSAGE("Diff nb of faces");
9598     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9599   }
9600
9601   // ============================================================
9602   // 2. Find nodes to merge:
9603   //              bind a node to remove to a node to put instead
9604   // ============================================================
9605
9606   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9607   if ( theFirstNode1 != theFirstNode2 )
9608     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9609   if ( theSecondNode1 != theSecondNode2 )
9610     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9611
9612   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9613   set< long > linkIdSet; // links to process
9614   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9615
9616   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9617   list< NLink > linkList[2];
9618   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9619   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9620   // loop on links in linkList; find faces by links and append links
9621   // of the found faces to linkList
9622   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9623   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9624   {
9625     NLink link[] = { *linkIt[0], *linkIt[1] };
9626     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9627     if ( !linkIdSet.count( linkID ) )
9628       continue;
9629
9630     // by links, find faces in the face sets,
9631     // and find indices of link nodes in the found faces;
9632     // in a face set, there is only one or no face sharing a link
9633     // ---------------------------------------------------------------
9634
9635     const SMDS_MeshElement* face[] = { 0, 0 };
9636     vector<const SMDS_MeshNode*> fnodes[2];
9637     int iLinkNode[2][2];
9638     TIDSortedElemSet avoidSet;
9639     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9640       const SMDS_MeshNode* n1 = link[iSide].first;
9641       const SMDS_MeshNode* n2 = link[iSide].second;
9642       //cout << "Side " << iSide << " ";
9643       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9644       // find a face by two link nodes
9645       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9646                                                       *faceSetPtr[ iSide ], avoidSet,
9647                                                       &iLinkNode[iSide][0],
9648                                                       &iLinkNode[iSide][1] );
9649       if ( face[ iSide ])
9650       {
9651         //cout << " F " << face[ iSide]->GetID() <<endl;
9652         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9653         // put face nodes to fnodes
9654         if ( face[ iSide ]->IsQuadratic() )
9655         {
9656           // use interlaced nodes iterator
9657           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9658           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9659           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9660           while ( nIter->more() )
9661             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9662         }
9663         else
9664         {
9665           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9666                                   face[ iSide ]->end_nodes() );
9667         }
9668         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9669       }
9670     }
9671
9672     // check similarity of elements of the sides
9673     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9674       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9675       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9676         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9677       }
9678       else {
9679         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9680       }
9681       break; // do not return because it's necessary to remove tmp faces
9682     }
9683
9684     // set nodes to merge
9685     // -------------------
9686
9687     if ( face[0] && face[1] )  {
9688       const int nbNodes = face[0]->NbNodes();
9689       if ( nbNodes != face[1]->NbNodes() ) {
9690         MESSAGE("Diff nb of face nodes");
9691         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9692         break; // do not return because it s necessary to remove tmp faces
9693       }
9694       bool reverse[] = { false, false }; // order of nodes in the link
9695       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9696         // analyse link orientation in faces
9697         int i1 = iLinkNode[ iSide ][ 0 ];
9698         int i2 = iLinkNode[ iSide ][ 1 ];
9699         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9700       }
9701       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9702       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9703       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9704       {
9705         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9706                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9707       }
9708
9709       // add other links of the faces to linkList
9710       // -----------------------------------------
9711
9712       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9713         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9714         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9715         if ( !iter_isnew.second ) { // already in a set: no need to process
9716           linkIdSet.erase( iter_isnew.first );
9717         }
9718         else // new in set == encountered for the first time: add
9719         {
9720           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9721           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9722           linkList[0].push_back ( NLink( n1, n2 ));
9723           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9724         }
9725       }
9726     } // 2 faces found
9727
9728     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9729       break;
9730
9731   } // loop on link lists
9732
9733   if ( aResult == SEW_OK &&
9734        ( //linkIt[0] != linkList[0].end() ||
9735          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9736     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9737              " " << (faceSetPtr[1]->empty()));
9738     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9739   }
9740
9741   // ====================================================================
9742   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9743   // ====================================================================
9744
9745   // delete temporary faces
9746 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9747 //  while ( tmpFaceIt->more() )
9748 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9749   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9750   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9751     aMesh->RemoveElement(*tmpFaceIt);
9752
9753   if ( aResult != SEW_OK)
9754     return aResult;
9755
9756   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9757   // loop on nodes replacement map
9758   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9759   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9760     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9761       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9762       nodeIDsToRemove.push_back( nToRemove->GetID() );
9763       // loop on elements sharing nToRemove
9764       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9765       while ( invElemIt->more() ) {
9766         const SMDS_MeshElement* e = invElemIt->next();
9767         // get a new suite of nodes: make replacement
9768         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9769         vector< const SMDS_MeshNode*> nodes( nbNodes );
9770         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9771         while ( nIt->more() ) {
9772           const SMDS_MeshNode* n =
9773             static_cast<const SMDS_MeshNode*>( nIt->next() );
9774           nnIt = nReplaceMap.find( n );
9775           if ( nnIt != nReplaceMap.end() ) {
9776             nbReplaced++;
9777             n = (*nnIt).second;
9778           }
9779           nodes[ i++ ] = n;
9780         }
9781         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9782         //         elemIDsToRemove.push_back( e->GetID() );
9783         //       else
9784         if ( nbReplaced )
9785           {
9786             SMDSAbs_ElementType etyp = e->GetType();
9787             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9788             if (newElem)
9789               {
9790                 myLastCreatedElems.Append(newElem);
9791                 AddToSameGroups(newElem, e, aMesh);
9792                 int aShapeId = e->getshapeId();
9793                 if ( aShapeId )
9794                   {
9795                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
9796                   }
9797               }
9798             aMesh->RemoveElement(e);
9799           }
9800       }
9801     }
9802
9803   Remove( nodeIDsToRemove, true );
9804
9805   return aResult;
9806 }
9807
9808 //================================================================================
9809 /*!
9810  * \brief Find corresponding nodes in two sets of faces
9811  * \param theSide1 - first face set
9812  * \param theSide2 - second first face
9813  * \param theFirstNode1 - a boundary node of set 1
9814  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9815  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9816  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9817  * \param nReplaceMap - output map of corresponding nodes
9818  * \return bool  - is a success or not
9819  */
9820 //================================================================================
9821
9822 #ifdef _DEBUG_
9823 //#define DEBUG_MATCHING_NODES
9824 #endif
9825
9826 SMESH_MeshEditor::Sew_Error
9827 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9828                                     set<const SMDS_MeshElement*>& theSide2,
9829                                     const SMDS_MeshNode*          theFirstNode1,
9830                                     const SMDS_MeshNode*          theFirstNode2,
9831                                     const SMDS_MeshNode*          theSecondNode1,
9832                                     const SMDS_MeshNode*          theSecondNode2,
9833                                     TNodeNodeMap &                nReplaceMap)
9834 {
9835   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9836
9837   nReplaceMap.clear();
9838   if ( theFirstNode1 != theFirstNode2 )
9839     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9840   if ( theSecondNode1 != theSecondNode2 )
9841     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9842
9843   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9844   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9845
9846   list< NLink > linkList[2];
9847   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9848   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9849
9850   // loop on links in linkList; find faces by links and append links
9851   // of the found faces to linkList
9852   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9853   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9854     NLink link[] = { *linkIt[0], *linkIt[1] };
9855     if ( linkSet.find( link[0] ) == linkSet.end() )
9856       continue;
9857
9858     // by links, find faces in the face sets,
9859     // and find indices of link nodes in the found faces;
9860     // in a face set, there is only one or no face sharing a link
9861     // ---------------------------------------------------------------
9862
9863     const SMDS_MeshElement* face[] = { 0, 0 };
9864     list<const SMDS_MeshNode*> notLinkNodes[2];
9865     //bool reverse[] = { false, false }; // order of notLinkNodes
9866     int nbNodes[2];
9867     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9868     {
9869       const SMDS_MeshNode* n1 = link[iSide].first;
9870       const SMDS_MeshNode* n2 = link[iSide].second;
9871       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9872       set< const SMDS_MeshElement* > facesOfNode1;
9873       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9874       {
9875         // during a loop of the first node, we find all faces around n1,
9876         // during a loop of the second node, we find one face sharing both n1 and n2
9877         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9878         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9879         while ( fIt->more() ) { // loop on faces sharing a node
9880           const SMDS_MeshElement* f = fIt->next();
9881           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9882               ! facesOfNode1.insert( f ).second ) // f encounters twice
9883           {
9884             if ( face[ iSide ] ) {
9885               MESSAGE( "2 faces per link " );
9886               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9887             }
9888             face[ iSide ] = f;
9889             faceSet->erase( f );
9890
9891             // get not link nodes
9892             int nbN = f->NbNodes();
9893             if ( f->IsQuadratic() )
9894               nbN /= 2;
9895             nbNodes[ iSide ] = nbN;
9896             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9897             int i1 = f->GetNodeIndex( n1 );
9898             int i2 = f->GetNodeIndex( n2 );
9899             int iEnd = nbN, iBeg = -1, iDelta = 1;
9900             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9901             if ( reverse ) {
9902               std::swap( iEnd, iBeg ); iDelta = -1;
9903             }
9904             int i = i2;
9905             while ( true ) {
9906               i += iDelta;
9907               if ( i == iEnd ) i = iBeg + iDelta;
9908               if ( i == i1 ) break;
9909               nodes.push_back ( f->GetNode( i ) );
9910             }
9911           }
9912         }
9913       }
9914     }
9915     // check similarity of elements of the sides
9916     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9917       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9918       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9919         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9920       }
9921       else {
9922         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9923       }
9924     }
9925
9926     // set nodes to merge
9927     // -------------------
9928
9929     if ( face[0] && face[1] )  {
9930       if ( nbNodes[0] != nbNodes[1] ) {
9931         MESSAGE("Diff nb of face nodes");
9932         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9933       }
9934 #ifdef DEBUG_MATCHING_NODES
9935       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9936                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9937                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9938 #endif
9939       int nbN = nbNodes[0];
9940       {
9941         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9942         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9943         for ( int i = 0 ; i < nbN - 2; ++i ) {
9944 #ifdef DEBUG_MATCHING_NODES
9945           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9946 #endif
9947           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9948         }
9949       }
9950
9951       // add other links of the face 1 to linkList
9952       // -----------------------------------------
9953
9954       const SMDS_MeshElement* f0 = face[0];
9955       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9956       for ( int i = 0; i < nbN; i++ )
9957       {
9958         const SMDS_MeshNode* n2 = f0->GetNode( i );
9959         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9960           linkSet.insert( SMESH_TLink( n1, n2 ));
9961         if ( !iter_isnew.second ) { // already in a set: no need to process
9962           linkSet.erase( iter_isnew.first );
9963         }
9964         else // new in set == encountered for the first time: add
9965         {
9966 #ifdef DEBUG_MATCHING_NODES
9967           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9968                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9969 #endif
9970           linkList[0].push_back ( NLink( n1, n2 ));
9971           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9972         }
9973         n1 = n2;
9974       }
9975     } // 2 faces found
9976   } // loop on link lists
9977
9978   return SEW_OK;
9979 }
9980
9981 //================================================================================
9982 /*!
9983  * \brief Create elements equal (on same nodes) to given ones
9984  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
9985  *              elements of the uppest dimension are duplicated.
9986  */
9987 //================================================================================
9988
9989 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9990 {
9991   CrearLastCreated();
9992   SMESHDS_Mesh* mesh = GetMeshDS();
9993
9994   // get an element type and an iterator over elements
9995
9996   SMDSAbs_ElementType type;
9997   SMDS_ElemIteratorPtr elemIt;
9998   vector< const SMDS_MeshElement* > allElems;
9999   if ( theElements.empty() )
10000   {
10001     if ( mesh->NbNodes() == 0 )
10002       return;
10003     // get most complex type
10004     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10005       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10006       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10007     };
10008     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10009       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10010       {
10011         type = types[i];
10012         break;
10013       }
10014     // put all elements in the vector <allElems>
10015     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10016     elemIt = mesh->elementsIterator( type );
10017     while ( elemIt->more() )
10018       allElems.push_back( elemIt->next());
10019     elemIt = elemSetIterator( allElems );
10020   }
10021   else
10022   {
10023     type = (*theElements.begin())->GetType();
10024     elemIt = elemSetIterator( theElements );
10025   }
10026
10027   // duplicate elements
10028
10029   if ( type == SMDSAbs_Ball )
10030   {
10031     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10032     while ( elemIt->more() )
10033     {
10034       const SMDS_MeshElement* elem = elemIt->next();
10035       if ( elem->GetType() != SMDSAbs_Ball )
10036         continue;
10037       if (( elem = mesh->AddBall( elem->GetNode(0),
10038                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10039         myLastCreatedElems.Append( elem );
10040     }
10041   }
10042   else
10043   {
10044     vector< const SMDS_MeshNode* > nodes;
10045     while ( elemIt->more() )
10046     {
10047       const SMDS_MeshElement* elem = elemIt->next();
10048       if ( elem->GetType() != type )
10049         continue;
10050
10051       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10052
10053       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10054       {
10055         std::vector<int> quantities =
10056           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10057         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10058       }
10059       else
10060       {
10061         AddElement( nodes, type, elem->IsPoly() );
10062         elem = 0; // myLastCreatedElems is already filled
10063       }
10064       if ( elem )
10065         myLastCreatedElems.Append( elem );
10066     }
10067   }
10068 }
10069
10070 //================================================================================
10071 /*!
10072   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10073   \param theElems - the list of elements (edges or faces) to be replicated
10074   The nodes for duplication could be found from these elements
10075   \param theNodesNot - list of nodes to NOT replicate
10076   \param theAffectedElems - the list of elements (cells and edges) to which the
10077   replicated nodes should be associated to.
10078   \return TRUE if operation has been completed successfully, FALSE otherwise
10079 */
10080 //================================================================================
10081
10082 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10083                                     const TIDSortedElemSet& theNodesNot,
10084                                     const TIDSortedElemSet& theAffectedElems )
10085 {
10086   myLastCreatedElems.Clear();
10087   myLastCreatedNodes.Clear();
10088
10089   if ( theElems.size() == 0 )
10090     return false;
10091
10092   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10093   if ( !aMeshDS )
10094     return false;
10095
10096   bool res = false;
10097   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10098   // duplicate elements and nodes
10099   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10100   // replce nodes by duplications
10101   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10102   return res;
10103 }
10104
10105 //================================================================================
10106 /*!
10107   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10108   \param theMeshDS - mesh instance
10109   \param theElems - the elements replicated or modified (nodes should be changed)
10110   \param theNodesNot - nodes to NOT replicate
10111   \param theNodeNodeMap - relation of old node to new created node
10112   \param theIsDoubleElem - flag os to replicate element or modify
10113   \return TRUE if operation has been completed successfully, FALSE otherwise
10114 */
10115 //================================================================================
10116
10117 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10118                                     const TIDSortedElemSet& theElems,
10119                                     const TIDSortedElemSet& theNodesNot,
10120                                     std::map< const SMDS_MeshNode*,
10121                                     const SMDS_MeshNode* >& theNodeNodeMap,
10122                                     const bool theIsDoubleElem )
10123 {
10124   MESSAGE("doubleNodes");
10125   // iterate on through element and duplicate them (by nodes duplication)
10126   bool res = false;
10127   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10128   for ( ;  elemItr != theElems.end(); ++elemItr )
10129   {
10130     const SMDS_MeshElement* anElem = *elemItr;
10131     if (!anElem)
10132       continue;
10133
10134     bool isDuplicate = false;
10135     // duplicate nodes to duplicate element
10136     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10137     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10138     int ind = 0;
10139     while ( anIter->more() )
10140     {
10141
10142       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10143       SMDS_MeshNode* aNewNode = aCurrNode;
10144       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10145         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10146       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10147       {
10148         // duplicate node
10149         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10150         theNodeNodeMap[ aCurrNode ] = aNewNode;
10151         myLastCreatedNodes.Append( aNewNode );
10152       }
10153       isDuplicate |= (aCurrNode != aNewNode);
10154       newNodes[ ind++ ] = aNewNode;
10155     }
10156     if ( !isDuplicate )
10157       continue;
10158
10159     if ( theIsDoubleElem )
10160       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10161     else
10162       {
10163       MESSAGE("ChangeElementNodes");
10164       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10165       }
10166     res = true;
10167   }
10168   return res;
10169 }
10170
10171 //================================================================================
10172 /*!
10173   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10174   \param theNodes - identifiers of nodes to be doubled
10175   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10176          nodes. If list of element identifiers is empty then nodes are doubled but
10177          they not assigned to elements
10178   \return TRUE if operation has been completed successfully, FALSE otherwise
10179 */
10180 //================================================================================
10181
10182 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10183                                     const std::list< int >& theListOfModifiedElems )
10184 {
10185   MESSAGE("DoubleNodes");
10186   myLastCreatedElems.Clear();
10187   myLastCreatedNodes.Clear();
10188
10189   if ( theListOfNodes.size() == 0 )
10190     return false;
10191
10192   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10193   if ( !aMeshDS )
10194     return false;
10195
10196   // iterate through nodes and duplicate them
10197
10198   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10199
10200   std::list< int >::const_iterator aNodeIter;
10201   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10202   {
10203     int aCurr = *aNodeIter;
10204     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10205     if ( !aNode )
10206       continue;
10207
10208     // duplicate node
10209
10210     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10211     if ( aNewNode )
10212     {
10213       anOldNodeToNewNode[ aNode ] = aNewNode;
10214       myLastCreatedNodes.Append( aNewNode );
10215     }
10216   }
10217
10218   // Create map of new nodes for modified elements
10219
10220   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10221
10222   std::list< int >::const_iterator anElemIter;
10223   for ( anElemIter = theListOfModifiedElems.begin();
10224         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10225   {
10226     int aCurr = *anElemIter;
10227     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10228     if ( !anElem )
10229       continue;
10230
10231     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10232
10233     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10234     int ind = 0;
10235     while ( anIter->more() )
10236     {
10237       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10238       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10239       {
10240         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10241         aNodeArr[ ind++ ] = aNewNode;
10242       }
10243       else
10244         aNodeArr[ ind++ ] = aCurrNode;
10245     }
10246     anElemToNodes[ anElem ] = aNodeArr;
10247   }
10248
10249   // Change nodes of elements
10250
10251   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10252     anElemToNodesIter = anElemToNodes.begin();
10253   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10254   {
10255     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10256     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10257     if ( anElem )
10258       {
10259       MESSAGE("ChangeElementNodes");
10260       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10261       }
10262   }
10263
10264   return true;
10265 }
10266
10267 namespace {
10268
10269   //================================================================================
10270   /*!
10271   \brief Check if element located inside shape
10272   \return TRUE if IN or ON shape, FALSE otherwise
10273   */
10274   //================================================================================
10275
10276   template<class Classifier>
10277   bool isInside(const SMDS_MeshElement* theElem,
10278                 Classifier&             theClassifier,
10279                 const double            theTol)
10280   {
10281     gp_XYZ centerXYZ (0, 0, 0);
10282     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10283     while (aNodeItr->more())
10284       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10285
10286     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10287     theClassifier.Perform(aPnt, theTol);
10288     TopAbs_State aState = theClassifier.State();
10289     return (aState == TopAbs_IN || aState == TopAbs_ON );
10290   }
10291
10292   //================================================================================
10293   /*!
10294    * \brief Classifier of the 3D point on the TopoDS_Face
10295    *        with interaface suitable for isInside()
10296    */
10297   //================================================================================
10298
10299   struct _FaceClassifier
10300   {
10301     Extrema_ExtPS       _extremum;
10302     BRepAdaptor_Surface _surface;
10303     TopAbs_State        _state;
10304
10305     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10306     {
10307       _extremum.Initialize( _surface,
10308                             _surface.FirstUParameter(), _surface.LastUParameter(),
10309                             _surface.FirstVParameter(), _surface.LastVParameter(),
10310                             _surface.Tolerance(), _surface.Tolerance() );
10311     }
10312     void Perform(const gp_Pnt& aPnt, double theTol)
10313     {
10314       _state = TopAbs_OUT;
10315       _extremum.Perform(aPnt);
10316       if ( _extremum.IsDone() )
10317         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10318 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10319           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10320 #else
10321           _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10322 #endif
10323     }
10324     TopAbs_State State() const
10325     {
10326       return _state;
10327     }
10328   };
10329 }
10330
10331 //================================================================================
10332 /*!
10333   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10334   This method is the first step of DoubleNodeElemGroupsInRegion.
10335   \param theElems - list of groups of elements (edges or faces) to be replicated
10336   \param theNodesNot - list of groups of nodes not to replicated
10337   \param theShape - shape to detect affected elements (element which geometric center
10338          located on or inside shape). If the shape is null, detection is done on faces orientations
10339          (select elements with a gravity center on the side given by faces normals).
10340          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10341          The replicated nodes should be associated to affected elements.
10342   \return groups of affected elements
10343   \sa DoubleNodeElemGroupsInRegion()
10344  */
10345 //================================================================================
10346
10347 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10348                                                    const TIDSortedElemSet& theNodesNot,
10349                                                    const TopoDS_Shape&     theShape,
10350                                                    TIDSortedElemSet&       theAffectedElems)
10351 {
10352   if ( theShape.IsNull() )
10353   {
10354     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10355     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10356     std::set<const SMDS_MeshElement*> edgesToCheck;
10357     alreadyCheckedNodes.clear();
10358     alreadyCheckedElems.clear();
10359     edgesToCheck.clear();
10360
10361     // --- iterates on elements to be replicated and get elements by back references from their nodes
10362
10363     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10364     int ielem;
10365     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10366     {
10367       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10368       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10369         continue;
10370       gp_XYZ normal;
10371       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10372       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10373       std::set<const SMDS_MeshNode*> nodesElem;
10374       nodesElem.clear();
10375       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10376       while ( nodeItr->more() )
10377       {
10378         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10379         nodesElem.insert(aNode);
10380       }
10381       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10382       for (; nodit != nodesElem.end(); nodit++)
10383       {
10384         MESSAGE("  noeud ");
10385         const SMDS_MeshNode* aNode = *nodit;
10386         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10387           continue;
10388         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10389           continue;
10390         alreadyCheckedNodes.insert(aNode);
10391         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10392         while ( backElemItr->more() )
10393         {
10394           MESSAGE("    backelem ");
10395           const SMDS_MeshElement* curElem = backElemItr->next();
10396           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10397             continue;
10398           if (theElems.find(curElem) != theElems.end())
10399             continue;
10400           alreadyCheckedElems.insert(curElem);
10401           double x=0, y=0, z=0;
10402           int nb = 0;
10403           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10404           while ( nodeItr2->more() )
10405           {
10406             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10407             x += anotherNode->X();
10408             y += anotherNode->Y();
10409             z += anotherNode->Z();
10410             nb++;
10411           }
10412           gp_XYZ p;
10413           p.SetCoord( x/nb -aNode->X(),
10414                       y/nb -aNode->Y(),
10415                       z/nb -aNode->Z() );
10416           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10417           if (normal*p > 0)
10418           {
10419             MESSAGE("    --- inserted")
10420             theAffectedElems.insert( curElem );
10421           }
10422           else if (curElem->GetType() == SMDSAbs_Edge)
10423             edgesToCheck.insert(curElem);
10424         }
10425       }
10426     }
10427     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10428     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10429     for( ; eit != edgesToCheck.end(); eit++)
10430     {
10431       bool onside = true;
10432       const SMDS_MeshElement* anEdge = *eit;
10433       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10434       while ( nodeItr->more() )
10435       {
10436         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10437         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10438         {
10439           onside = false;
10440           break;
10441         }
10442       }
10443       if (onside)
10444       {
10445         MESSAGE("    --- edge onside inserted")
10446         theAffectedElems.insert(anEdge);
10447       }
10448     }
10449   }
10450   else
10451   {
10452     const double aTol = Precision::Confusion();
10453     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10454     auto_ptr<_FaceClassifier>              aFaceClassifier;
10455     if ( theShape.ShapeType() == TopAbs_SOLID )
10456     {
10457       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10458       bsc3d->PerformInfinitePoint(aTol);
10459     }
10460     else if (theShape.ShapeType() == TopAbs_FACE )
10461     {
10462       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10463     }
10464
10465     // iterates on indicated elements and get elements by back references from their nodes
10466     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10467     int ielem;
10468     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10469     {
10470       MESSAGE("element " << ielem++);
10471       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10472       if (!anElem)
10473         continue;
10474       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10475       while ( nodeItr->more() )
10476       {
10477         MESSAGE("  noeud ");
10478         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10479         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10480           continue;
10481         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10482         while ( backElemItr->more() )
10483         {
10484           MESSAGE("    backelem ");
10485           const SMDS_MeshElement* curElem = backElemItr->next();
10486           if ( curElem && theElems.find(curElem) == theElems.end() &&
10487               ( bsc3d.get() ?
10488                 isInside( curElem, *bsc3d, aTol ) :
10489                 isInside( curElem, *aFaceClassifier, aTol )))
10490             theAffectedElems.insert( curElem );
10491         }
10492       }
10493     }
10494   }
10495   return true;
10496 }
10497
10498 //================================================================================
10499 /*!
10500   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10501   \param theElems - group of of elements (edges or faces) to be replicated
10502   \param theNodesNot - group of nodes not to replicate
10503   \param theShape - shape to detect affected elements (element which geometric center
10504   located on or inside shape).
10505   The replicated nodes should be associated to affected elements.
10506   \return TRUE if operation has been completed successfully, FALSE otherwise
10507 */
10508 //================================================================================
10509
10510 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10511                                             const TIDSortedElemSet& theNodesNot,
10512                                             const TopoDS_Shape&     theShape )
10513 {
10514   if ( theShape.IsNull() )
10515     return false;
10516
10517   const double aTol = Precision::Confusion();
10518   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10519   auto_ptr<_FaceClassifier>              aFaceClassifier;
10520   if ( theShape.ShapeType() == TopAbs_SOLID )
10521   {
10522     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10523     bsc3d->PerformInfinitePoint(aTol);
10524   }
10525   else if (theShape.ShapeType() == TopAbs_FACE )
10526   {
10527     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10528   }
10529
10530   // iterates on indicated elements and get elements by back references from their nodes
10531   TIDSortedElemSet anAffected;
10532   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10533   for ( ;  elemItr != theElems.end(); ++elemItr )
10534   {
10535     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10536     if (!anElem)
10537       continue;
10538
10539     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10540     while ( nodeItr->more() )
10541     {
10542       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10543       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10544         continue;
10545       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10546       while ( backElemItr->more() )
10547       {
10548         const SMDS_MeshElement* curElem = backElemItr->next();
10549         if ( curElem && theElems.find(curElem) == theElems.end() &&
10550              ( bsc3d.get() ?
10551                isInside( curElem, *bsc3d, aTol ) :
10552                isInside( curElem, *aFaceClassifier, aTol )))
10553           anAffected.insert( curElem );
10554       }
10555     }
10556   }
10557   return DoubleNodes( theElems, theNodesNot, anAffected );
10558 }
10559
10560 /*!
10561  *  \brief compute an oriented angle between two planes defined by four points.
10562  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10563  *  @param p0 base of the rotation axe
10564  *  @param p1 extremity of the rotation axe
10565  *  @param g1 belongs to the first plane
10566  *  @param g2 belongs to the second plane
10567  */
10568 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10569 {
10570 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10571 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10572 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10573 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10574   gp_Vec vref(p0, p1);
10575   gp_Vec v1(p0, g1);
10576   gp_Vec v2(p0, g2);
10577   gp_Vec n1 = vref.Crossed(v1);
10578   gp_Vec n2 = vref.Crossed(v2);
10579   try {
10580     return n2.AngleWithRef(n1, vref);
10581   }
10582   catch ( Standard_Failure ) {
10583   }
10584   return Max( v1.Magnitude(), v2.Magnitude() );
10585 }
10586
10587 /*!
10588  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10589  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10590  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10591  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10592  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10593  * the group j_n_p is the group of the flat elements that are built between the group #n and the group #p in the list.
10594  * If there is no shared faces between the group #n and the group #p in the list, the group j_n_p is not created.
10595  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10596  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10597  * \param theElems - list of groups of volumes, where a group of volume is a set of
10598  *        SMDS_MeshElements sorted by Id.
10599  * \param createJointElems - if TRUE, create the elements
10600  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10601  *        the boundary between \a theDomains and the rest mesh
10602  * \return TRUE if operation has been completed successfully, FALSE otherwise
10603  */
10604 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10605                                                      bool                                 createJointElems,
10606                                                      bool                                 onAllBoundaries)
10607 {
10608   MESSAGE("----------------------------------------------");
10609   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10610   MESSAGE("----------------------------------------------");
10611
10612   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10613   meshDS->BuildDownWardConnectivity(true);
10614   CHRONO(50);
10615   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10616
10617   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10618   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10619   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10620
10621   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10622   std::map<int,int>celldom; // cell vtkId --> domain
10623   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10624   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10625   faceDomains.clear();
10626   celldom.clear();
10627   cellDomains.clear();
10628   nodeDomains.clear();
10629   std::map<int,int> emptyMap;
10630   std::set<int> emptySet;
10631   emptyMap.clear();
10632
10633   MESSAGE(".. Number of domains :"<<theElems.size());
10634
10635   TIDSortedElemSet theRestDomElems;
10636   const int iRestDom  = -1;
10637   const int idom0     = onAllBoundaries ? iRestDom : 0;
10638   const int nbDomains = theElems.size();
10639
10640   // Check if the domains do not share an element
10641   for (int idom = 0; idom < nbDomains-1; idom++)
10642     {
10643 //       MESSAGE("... Check of domain #" << idom);
10644       const TIDSortedElemSet& domain = theElems[idom];
10645       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10646       for (; elemItr != domain.end(); ++elemItr)
10647         {
10648           const SMDS_MeshElement* anElem = *elemItr;
10649           int idombisdeb = idom + 1 ;
10650           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10651           {
10652             const TIDSortedElemSet& domainbis = theElems[idombis];
10653             if ( domainbis.count(anElem) )
10654             {
10655               MESSAGE(".... Domain #" << idom);
10656               MESSAGE(".... Domain #" << idombis);
10657               throw SALOME_Exception("The domains are not disjoint.");
10658               return false ;
10659             }
10660           }
10661         }
10662     }
10663
10664   for (int idom = 0; idom < nbDomains; idom++)
10665     {
10666
10667       // --- build a map (face to duplicate --> volume to modify)
10668       //     with all the faces shared by 2 domains (group of elements)
10669       //     and corresponding volume of this domain, for each shared face.
10670       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10671
10672       MESSAGE("... Neighbors of domain #" << idom);
10673       const TIDSortedElemSet& domain = theElems[idom];
10674       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10675       for (; elemItr != domain.end(); ++elemItr)
10676         {
10677           const SMDS_MeshElement* anElem = *elemItr;
10678           if (!anElem)
10679             continue;
10680           int vtkId = anElem->getVtkId();
10681           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
10682           int neighborsVtkIds[NBMAXNEIGHBORS];
10683           int downIds[NBMAXNEIGHBORS];
10684           unsigned char downTypes[NBMAXNEIGHBORS];
10685           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10686           for (int n = 0; n < nbNeighbors; n++)
10687             {
10688               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10689               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10690               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10691                 {
10692                   bool ok = false ;
10693                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10694                   {
10695                     // MESSAGE("Domain " << idombis);
10696                     const TIDSortedElemSet& domainbis = theElems[idombis];
10697                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10698                   }
10699                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10700                   {
10701                     DownIdType face(downIds[n], downTypes[n]);
10702                     if (!faceDomains[face].count(idom))
10703                       {
10704                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10705                         celldom[vtkId] = idom;
10706                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
10707                       }
10708                     if ( !ok )
10709                     {
10710                       theRestDomElems.insert( elem );
10711                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
10712                       celldom[neighborsVtkIds[n]] = iRestDom;
10713                     }
10714                   }
10715                 }
10716             }
10717         }
10718     }
10719
10720   //MESSAGE("Number of shared faces " << faceDomains.size());
10721   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10722
10723   // --- explore the shared faces domain by domain,
10724   //     explore the nodes of the face and see if they belong to a cell in the domain,
10725   //     which has only a node or an edge on the border (not a shared face)
10726
10727   for (int idomain = idom0; idomain < nbDomains; idomain++)
10728     {
10729       //MESSAGE("Domain " << idomain);
10730       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10731       itface = faceDomains.begin();
10732       for (; itface != faceDomains.end(); ++itface)
10733         {
10734           const std::map<int, int>& domvol = itface->second;
10735           if (!domvol.count(idomain))
10736             continue;
10737           DownIdType face = itface->first;
10738           //MESSAGE(" --- face " << face.cellId);
10739           std::set<int> oldNodes;
10740           oldNodes.clear();
10741           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10742           std::set<int>::iterator itn = oldNodes.begin();
10743           for (; itn != oldNodes.end(); ++itn)
10744             {
10745               int oldId = *itn;
10746               //MESSAGE("     node " << oldId);
10747               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10748               for (int i=0; i<l.ncells; i++)
10749                 {
10750                   int vtkId = l.cells[i];
10751                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10752                   if (!domain.count(anElem))
10753                     continue;
10754                   int vtkType = grid->GetCellType(vtkId);
10755                   int downId = grid->CellIdToDownId(vtkId);
10756                   if (downId < 0)
10757                     {
10758                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10759                       continue; // not OK at this stage of the algorithm:
10760                                 //no cells created after BuildDownWardConnectivity
10761                     }
10762                   DownIdType aCell(downId, vtkType);
10763                   cellDomains[aCell][idomain] = vtkId;
10764                   celldom[vtkId] = idomain;
10765                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
10766                 }
10767             }
10768         }
10769     }
10770
10771   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10772   //     for each shared face, get the nodes
10773   //     for each node, for each domain of the face, create a clone of the node
10774
10775   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10776   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10777   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
10778
10779   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10780   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10781   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10782
10783   MESSAGE(".. Duplication of the nodes");
10784   for (int idomain = idom0; idomain < nbDomains; idomain++)
10785     {
10786       itface = faceDomains.begin();
10787       for (; itface != faceDomains.end(); ++itface)
10788         {
10789           const std::map<int, int>& domvol = itface->second;
10790           if (!domvol.count(idomain))
10791             continue;
10792           DownIdType face = itface->first;
10793           //MESSAGE(" --- face " << face.cellId);
10794           std::set<int> oldNodes;
10795           oldNodes.clear();
10796           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10797           std::set<int>::iterator itn = oldNodes.begin();
10798           for (; itn != oldNodes.end(); ++itn)
10799             {
10800               int oldId = *itn;
10801               if (nodeDomains[oldId].empty())
10802                 {
10803                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10804                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
10805                 }
10806               std::map<int, int>::const_iterator itdom = domvol.begin();
10807               for (; itdom != domvol.end(); ++itdom)
10808                 {
10809                   int idom = itdom->first;
10810                   //MESSAGE("         domain " << idom);
10811                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
10812                     {
10813                       if (nodeDomains[oldId].size() >= 2) // a multiple node
10814                         {
10815                           vector<int> orderedDoms;
10816                           //MESSAGE("multiple node " << oldId);
10817                           if (mutipleNodes.count(oldId))
10818                             orderedDoms = mutipleNodes[oldId];
10819                           else
10820                             {
10821                               map<int,int>::iterator it = nodeDomains[oldId].begin();
10822                               for (; it != nodeDomains[oldId].end(); ++it)
10823                                 orderedDoms.push_back(it->first);
10824                             }
10825                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
10826                           //stringstream txt;
10827                           //for (int i=0; i<orderedDoms.size(); i++)
10828                           //  txt << orderedDoms[i] << " ";
10829                           //MESSAGE("orderedDoms " << txt.str());
10830                           mutipleNodes[oldId] = orderedDoms;
10831                         }
10832                       double *coords = grid->GetPoint(oldId);
10833                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10834                       int newId = newNode->getVtkId();
10835                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
10836                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10837                     }
10838                 }
10839             }
10840         }
10841     }
10842
10843   MESSAGE(".. Creation of elements");
10844   for (int idomain = idom0; idomain < nbDomains; idomain++)
10845     {
10846       itface = faceDomains.begin();
10847       for (; itface != faceDomains.end(); ++itface)
10848         {
10849           std::map<int, int> domvol = itface->second;
10850           if (!domvol.count(idomain))
10851             continue;
10852           DownIdType face = itface->first;
10853           //MESSAGE(" --- face " << face.cellId);
10854           std::set<int> oldNodes;
10855           oldNodes.clear();
10856           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10857           int nbMultipleNodes = 0;
10858           std::set<int>::iterator itn = oldNodes.begin();
10859           for (; itn != oldNodes.end(); ++itn)
10860             {
10861               int oldId = *itn;
10862               if (mutipleNodes.count(oldId))
10863                 nbMultipleNodes++;
10864             }
10865           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10866             {
10867               //MESSAGE("multiple Nodes detected on a shared face");
10868               int downId = itface->first.cellId;
10869               unsigned char cellType = itface->first.cellType;
10870               // --- shared edge or shared face ?
10871               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10872                 {
10873                   int nodes[3];
10874                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10875                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10876                     if (mutipleNodes.count(nodes[i]))
10877                       if (!mutipleNodesToFace.count(nodes[i]))
10878                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10879                 }
10880               else // shared face (between two volumes)
10881                 {
10882                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10883                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10884                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10885                   for (int ie =0; ie < nbEdges; ie++)
10886                     {
10887                       int nodes[3];
10888                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10889                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10890                         {
10891                           vector<int> vn0 = mutipleNodes[nodes[0]];
10892                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10893                           vector<int> doms;
10894                           for (int i0 = 0; i0 < vn0.size(); i0++)
10895                             for (int i1 = 0; i1 < vn1.size(); i1++)
10896                               if (vn0[i0] == vn1[i1])
10897                                 doms.push_back(vn0[i0]);
10898                           if (doms.size() >2)
10899                             {
10900                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10901                               double *coords = grid->GetPoint(nodes[0]);
10902                               gp_Pnt p0(coords[0], coords[1], coords[2]);
10903                               coords = grid->GetPoint(nodes[nbNodes - 1]);
10904                               gp_Pnt p1(coords[0], coords[1], coords[2]);
10905                               gp_Pnt gref;
10906                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
10907                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10908                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10909                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10910                               for (int id=0; id < doms.size(); id++)
10911                                 {
10912                                   int idom = doms[id];
10913                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10914                                   for (int ivol=0; ivol<nbvol; ivol++)
10915                                     {
10916                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10917                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10918                                       if (domain.count(elem))
10919                                         {
10920                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10921                                           domvol[idom] = svol;
10922                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
10923                                           double values[3];
10924                                           vtkIdType npts = 0;
10925                                           vtkIdType* pts = 0;
10926                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10927                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10928                                           if (id ==0)
10929                                             {
10930                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10931                                               angleDom[idom] = 0;
10932                                             }
10933                                           else
10934                                             {
10935                                               gp_Pnt g(values[0], values[1], values[2]);
10936                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10937                                               //MESSAGE("  angle=" << angleDom[idom]);
10938                                             }
10939                                           break;
10940                                         }
10941                                     }
10942                                 }
10943                               map<double, int> sortedDom; // sort domains by angle
10944                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10945                                 sortedDom[ia->second] = ia->first;
10946                               vector<int> vnodes;
10947                               vector<int> vdom;
10948                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10949                                 {
10950                                   vdom.push_back(ib->second);
10951                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
10952                                 }
10953                               for (int ino = 0; ino < nbNodes; ino++)
10954                                 vnodes.push_back(nodes[ino]);
10955                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10956                             }
10957                         }
10958                     }
10959                 }
10960             }
10961         }
10962     }
10963
10964   // --- iterate on shared faces (volumes to modify, face to extrude)
10965   //     get node id's of the face (id SMDS = id VTK)
10966   //     create flat element with old and new nodes if requested
10967
10968   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10969   //     (domain1 X domain2) = domain1 + MAXINT*domain2
10970
10971   std::map<int, std::map<long,int> > nodeQuadDomains;
10972   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10973
10974   MESSAGE(".. Creation of elements: simple junction");
10975   if (createJointElems)
10976     {
10977       int idg;
10978       string joints2DName = "joints2D";
10979       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10980       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10981       string joints3DName = "joints3D";
10982       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10983       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10984
10985       itface = faceDomains.begin();
10986       for (; itface != faceDomains.end(); ++itface)
10987         {
10988           DownIdType face = itface->first;
10989           std::set<int> oldNodes;
10990           std::set<int>::iterator itn;
10991           oldNodes.clear();
10992           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10993
10994           std::map<int, int> domvol = itface->second;
10995           std::map<int, int>::iterator itdom = domvol.begin();
10996           int dom1 = itdom->first;
10997           int vtkVolId = itdom->second;
10998           itdom++;
10999           int dom2 = itdom->first;
11000           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11001                                                              nodeQuadDomains);
11002           stringstream grpname;
11003           grpname << "j_";
11004           if (dom1 < dom2)
11005             grpname << dom1 << "_" << dom2;
11006           else
11007             grpname << dom2 << "_" << dom1;
11008           string namegrp = grpname.str();
11009           if (!mapOfJunctionGroups.count(namegrp))
11010             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11011           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11012           if (sgrp)
11013             sgrp->Add(vol->GetID());
11014           if (vol->GetType() == SMDSAbs_Volume)
11015             joints3DGrp->Add(vol->GetID());
11016           else if (vol->GetType() == SMDSAbs_Face)
11017             joints2DGrp->Add(vol->GetID());
11018         }
11019     }
11020
11021   // --- create volumes on multiple domain intersection if requested
11022   //     iterate on mutipleNodesToFace
11023   //     iterate on edgesMultiDomains
11024
11025   MESSAGE(".. Creation of elements: multiple junction");
11026   if (createJointElems)
11027     {
11028       // --- iterate on mutipleNodesToFace
11029
11030       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11031       for (; itn != mutipleNodesToFace.end(); ++itn)
11032         {
11033           int node = itn->first;
11034           vector<int> orderDom = itn->second;
11035           vector<vtkIdType> orderedNodes;
11036           for (int idom = 0; idom <orderDom.size(); idom++)
11037             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11038             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11039
11040             stringstream grpname;
11041             grpname << "m2j_";
11042             grpname << 0 << "_" << 0;
11043             int idg;
11044             string namegrp = grpname.str();
11045             if (!mapOfJunctionGroups.count(namegrp))
11046               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11047             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11048             if (sgrp)
11049               sgrp->Add(face->GetID());
11050         }
11051
11052       // --- iterate on edgesMultiDomains
11053
11054       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11055       for (; ite != edgesMultiDomains.end(); ++ite)
11056         {
11057           vector<int> nodes = ite->first;
11058           vector<int> orderDom = ite->second;
11059           vector<vtkIdType> orderedNodes;
11060           if (nodes.size() == 2)
11061             {
11062               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11063               for (int ino=0; ino < nodes.size(); ino++)
11064                 if (orderDom.size() == 3)
11065                   for (int idom = 0; idom <orderDom.size(); idom++)
11066                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11067                 else
11068                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11069                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11070               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11071
11072               int idg;
11073               string namegrp = "jointsMultiples";
11074               if (!mapOfJunctionGroups.count(namegrp))
11075                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11076               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11077               if (sgrp)
11078                 sgrp->Add(vol->GetID());
11079             }
11080           else
11081             {
11082               INFOS("Quadratic multiple joints not implemented");
11083               // TODO quadratic nodes
11084             }
11085         }
11086     }
11087
11088   // --- list the explicit faces and edges of the mesh that need to be modified,
11089   //     i.e. faces and edges built with one or more duplicated nodes.
11090   //     associate these faces or edges to their corresponding domain.
11091   //     only the first domain found is kept when a face or edge is shared
11092
11093   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11094   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11095   faceOrEdgeDom.clear();
11096   feDom.clear();
11097
11098   MESSAGE(".. Modification of elements");
11099   for (int idomain = idom0; idomain < nbDomains; idomain++)
11100     {
11101       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11102       for (; itnod != nodeDomains.end(); ++itnod)
11103         {
11104           int oldId = itnod->first;
11105           //MESSAGE("     node " << oldId);
11106           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11107           for (int i = 0; i < l.ncells; i++)
11108             {
11109               int vtkId = l.cells[i];
11110               int vtkType = grid->GetCellType(vtkId);
11111               int downId = grid->CellIdToDownId(vtkId);
11112               if (downId < 0)
11113                 continue; // new cells: not to be modified
11114               DownIdType aCell(downId, vtkType);
11115               int volParents[1000];
11116               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11117               for (int j = 0; j < nbvol; j++)
11118                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11119                   if (!feDom.count(vtkId))
11120                     {
11121                       feDom[vtkId] = idomain;
11122                       faceOrEdgeDom[aCell] = emptyMap;
11123                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11124                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11125                       //        << " type " << vtkType << " downId " << downId);
11126                     }
11127             }
11128         }
11129     }
11130
11131   // --- iterate on shared faces (volumes to modify, face to extrude)
11132   //     get node id's of the face
11133   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11134
11135   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11136   for (int m=0; m<3; m++)
11137     {
11138       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11139       itface = (*amap).begin();
11140       for (; itface != (*amap).end(); ++itface)
11141         {
11142           DownIdType face = itface->first;
11143           std::set<int> oldNodes;
11144           std::set<int>::iterator itn;
11145           oldNodes.clear();
11146           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11147           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11148           std::map<int, int> localClonedNodeIds;
11149
11150           std::map<int, int> domvol = itface->second;
11151           std::map<int, int>::iterator itdom = domvol.begin();
11152           for (; itdom != domvol.end(); ++itdom)
11153             {
11154               int idom = itdom->first;
11155               int vtkVolId = itdom->second;
11156               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11157               localClonedNodeIds.clear();
11158               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11159                 {
11160                   int oldId = *itn;
11161                   if (nodeDomains[oldId].count(idom))
11162                     {
11163                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11164                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11165                     }
11166                 }
11167               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11168             }
11169         }
11170     }
11171
11172   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11173   grid->BuildLinks();
11174
11175   CHRONOSTOP(50);
11176   counters::stats();
11177   return true;
11178 }
11179
11180 /*!
11181  * \brief Double nodes on some external faces and create flat elements.
11182  * Flat elements are mainly used by some types of mechanic calculations.
11183  *
11184  * Each group of the list must be constituted of faces.
11185  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11186  * @param theElems - list of groups of faces, where a group of faces is a set of
11187  * SMDS_MeshElements sorted by Id.
11188  * @return TRUE if operation has been completed successfully, FALSE otherwise
11189  */
11190 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11191 {
11192   MESSAGE("-------------------------------------------------");
11193   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11194   MESSAGE("-------------------------------------------------");
11195
11196   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11197
11198   // --- For each group of faces
11199   //     duplicate the nodes, create a flat element based on the face
11200   //     replace the nodes of the faces by their clones
11201
11202   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11203   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11204   clonedNodes.clear();
11205   intermediateNodes.clear();
11206   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11207   mapOfJunctionGroups.clear();
11208
11209   for (int idom = 0; idom < theElems.size(); idom++)
11210     {
11211       const TIDSortedElemSet& domain = theElems[idom];
11212       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11213       for (; elemItr != domain.end(); ++elemItr)
11214         {
11215           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11216           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11217           if (!aFace)
11218             continue;
11219           // MESSAGE("aFace=" << aFace->GetID());
11220           bool isQuad = aFace->IsQuadratic();
11221           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11222
11223           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11224
11225           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11226           while (nodeIt->more())
11227             {
11228               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11229               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11230               if (isMedium)
11231                 ln2.push_back(node);
11232               else
11233                 ln0.push_back(node);
11234
11235               const SMDS_MeshNode* clone = 0;
11236               if (!clonedNodes.count(node))
11237                 {
11238                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11239                   clonedNodes[node] = clone;
11240                 }
11241               else
11242                 clone = clonedNodes[node];
11243
11244               if (isMedium)
11245                 ln3.push_back(clone);
11246               else
11247                 ln1.push_back(clone);
11248
11249               const SMDS_MeshNode* inter = 0;
11250               if (isQuad && (!isMedium))
11251                 {
11252                   if (!intermediateNodes.count(node))
11253                     {
11254                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11255                       intermediateNodes[node] = inter;
11256                     }
11257                   else
11258                     inter = intermediateNodes[node];
11259                   ln4.push_back(inter);
11260                 }
11261             }
11262
11263           // --- extrude the face
11264
11265           vector<const SMDS_MeshNode*> ln;
11266           SMDS_MeshVolume* vol = 0;
11267           vtkIdType aType = aFace->GetVtkType();
11268           switch (aType)
11269           {
11270             case VTK_TRIANGLE:
11271               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11272               // MESSAGE("vol prism " << vol->GetID());
11273               ln.push_back(ln1[0]);
11274               ln.push_back(ln1[1]);
11275               ln.push_back(ln1[2]);
11276               break;
11277             case VTK_QUAD:
11278               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11279               // MESSAGE("vol hexa " << vol->GetID());
11280               ln.push_back(ln1[0]);
11281               ln.push_back(ln1[1]);
11282               ln.push_back(ln1[2]);
11283               ln.push_back(ln1[3]);
11284               break;
11285             case VTK_QUADRATIC_TRIANGLE:
11286               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11287                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11288               // MESSAGE("vol quad prism " << vol->GetID());
11289               ln.push_back(ln1[0]);
11290               ln.push_back(ln1[1]);
11291               ln.push_back(ln1[2]);
11292               ln.push_back(ln3[0]);
11293               ln.push_back(ln3[1]);
11294               ln.push_back(ln3[2]);
11295               break;
11296             case VTK_QUADRATIC_QUAD:
11297 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11298 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11299 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11300               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11301                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11302                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11303               // MESSAGE("vol quad hexa " << vol->GetID());
11304               ln.push_back(ln1[0]);
11305               ln.push_back(ln1[1]);
11306               ln.push_back(ln1[2]);
11307               ln.push_back(ln1[3]);
11308               ln.push_back(ln3[0]);
11309               ln.push_back(ln3[1]);
11310               ln.push_back(ln3[2]);
11311               ln.push_back(ln3[3]);
11312               break;
11313             case VTK_POLYGON:
11314               break;
11315             default:
11316               break;
11317           }
11318
11319           if (vol)
11320             {
11321               stringstream grpname;
11322               grpname << "jf_";
11323               grpname << idom;
11324               int idg;
11325               string namegrp = grpname.str();
11326               if (!mapOfJunctionGroups.count(namegrp))
11327                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11328               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11329               if (sgrp)
11330                 sgrp->Add(vol->GetID());
11331             }
11332
11333           // --- modify the face
11334
11335           aFace->ChangeNodes(&ln[0], ln.size());
11336         }
11337     }
11338   return true;
11339 }
11340
11341 /*!
11342  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11343  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11344  *  groups of faces to remove inside the object, (idem edges).
11345  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11346  */
11347 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11348                                       const TopoDS_Shape& theShape,
11349                                       SMESH_NodeSearcher* theNodeSearcher,
11350                                       const char* groupName,
11351                                       std::vector<double>&   nodesCoords,
11352                                       std::vector<std::vector<int> >& listOfListOfNodes)
11353 {
11354   MESSAGE("--------------------------------");
11355   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11356   MESSAGE("--------------------------------");
11357
11358   // --- zone of volumes to remove is given :
11359   //     1 either by a geom shape (one or more vertices) and a radius,
11360   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11361   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11362   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11363   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11364   //     defined by it's name.
11365
11366   SMESHDS_GroupBase* groupDS = 0;
11367   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11368   while ( groupIt->more() )
11369     {
11370       groupDS = 0;
11371       SMESH_Group * group = groupIt->next();
11372       if ( !group ) continue;
11373       groupDS = group->GetGroupDS();
11374       if ( !groupDS || groupDS->IsEmpty() ) continue;
11375       std::string grpName = group->GetName();
11376       //MESSAGE("grpName=" << grpName);
11377       if (grpName == groupName)
11378         break;
11379       else
11380         groupDS = 0;
11381     }
11382
11383   bool isNodeGroup = false;
11384   bool isNodeCoords = false;
11385   if (groupDS)
11386     {
11387       if (groupDS->GetType() != SMDSAbs_Node)
11388         return;
11389       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11390     }
11391
11392   if (nodesCoords.size() > 0)
11393     isNodeCoords = true; // a list o nodes given by their coordinates
11394   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11395
11396   // --- define groups to build
11397
11398   int idg; // --- group of SMDS volumes
11399   string grpvName = groupName;
11400   grpvName += "_vol";
11401   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11402   if (!grp)
11403     {
11404       MESSAGE("group not created " << grpvName);
11405       return;
11406     }
11407   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11408
11409   int idgs; // --- group of SMDS faces on the skin
11410   string grpsName = groupName;
11411   grpsName += "_skin";
11412   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11413   if (!grps)
11414     {
11415       MESSAGE("group not created " << grpsName);
11416       return;
11417     }
11418   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11419
11420   int idgi; // --- group of SMDS faces internal (several shapes)
11421   string grpiName = groupName;
11422   grpiName += "_internalFaces";
11423   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11424   if (!grpi)
11425     {
11426       MESSAGE("group not created " << grpiName);
11427       return;
11428     }
11429   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11430
11431   int idgei; // --- group of SMDS faces internal (several shapes)
11432   string grpeiName = groupName;
11433   grpeiName += "_internalEdges";
11434   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11435   if (!grpei)
11436     {
11437       MESSAGE("group not created " << grpeiName);
11438       return;
11439     }
11440   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11441
11442   // --- build downward connectivity
11443
11444   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11445   meshDS->BuildDownWardConnectivity(true);
11446   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11447
11448   // --- set of volumes detected inside
11449
11450   std::set<int> setOfInsideVol;
11451   std::set<int> setOfVolToCheck;
11452
11453   std::vector<gp_Pnt> gpnts;
11454   gpnts.clear();
11455
11456   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11457     {
11458       MESSAGE("group of nodes provided");
11459       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11460       while ( elemIt->more() )
11461         {
11462           const SMDS_MeshElement* elem = elemIt->next();
11463           if (!elem)
11464             continue;
11465           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11466           if (!node)
11467             continue;
11468           SMDS_MeshElement* vol = 0;
11469           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11470           while (volItr->more())
11471             {
11472               vol = (SMDS_MeshElement*)volItr->next();
11473               setOfInsideVol.insert(vol->getVtkId());
11474               sgrp->Add(vol->GetID());
11475             }
11476         }
11477     }
11478   else if (isNodeCoords)
11479     {
11480       MESSAGE("list of nodes coordinates provided");
11481       int i = 0;
11482       int k = 0;
11483       while (i < nodesCoords.size()-2)
11484         {
11485           double x = nodesCoords[i++];
11486           double y = nodesCoords[i++];
11487           double z = nodesCoords[i++];
11488           gp_Pnt p = gp_Pnt(x, y ,z);
11489           gpnts.push_back(p);
11490           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11491           k++;
11492         }
11493     }
11494   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11495     {
11496       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11497       TopTools_IndexedMapOfShape vertexMap;
11498       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11499       gp_Pnt p = gp_Pnt(0,0,0);
11500       if (vertexMap.Extent() < 1)
11501         return;
11502
11503       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11504         {
11505           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11506           p = BRep_Tool::Pnt(vertex);
11507           gpnts.push_back(p);
11508           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11509         }
11510     }
11511
11512   if (gpnts.size() > 0)
11513     {
11514       int nodeId = 0;
11515       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11516       if (startNode)
11517         nodeId = startNode->GetID();
11518       MESSAGE("nodeId " << nodeId);
11519
11520       double radius2 = radius*radius;
11521       MESSAGE("radius2 " << radius2);
11522
11523       // --- volumes on start node
11524
11525       setOfVolToCheck.clear();
11526       SMDS_MeshElement* startVol = 0;
11527       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11528       while (volItr->more())
11529         {
11530           startVol = (SMDS_MeshElement*)volItr->next();
11531           setOfVolToCheck.insert(startVol->getVtkId());
11532         }
11533       if (setOfVolToCheck.empty())
11534         {
11535           MESSAGE("No volumes found");
11536           return;
11537         }
11538
11539       // --- starting with central volumes then their neighbors, check if they are inside
11540       //     or outside the domain, until no more new neighbor volume is inside.
11541       //     Fill the group of inside volumes
11542
11543       std::map<int, double> mapOfNodeDistance2;
11544       mapOfNodeDistance2.clear();
11545       std::set<int> setOfOutsideVol;
11546       while (!setOfVolToCheck.empty())
11547         {
11548           std::set<int>::iterator it = setOfVolToCheck.begin();
11549           int vtkId = *it;
11550           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11551           bool volInside = false;
11552           vtkIdType npts = 0;
11553           vtkIdType* pts = 0;
11554           grid->GetCellPoints(vtkId, npts, pts);
11555           for (int i=0; i<npts; i++)
11556             {
11557               double distance2 = 0;
11558               if (mapOfNodeDistance2.count(pts[i]))
11559                 {
11560                   distance2 = mapOfNodeDistance2[pts[i]];
11561                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11562                 }
11563               else
11564                 {
11565                   double *coords = grid->GetPoint(pts[i]);
11566                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11567                   distance2 = 1.E40;
11568                   for (int j=0; j<gpnts.size(); j++)
11569                     {
11570                       double d2 = aPoint.SquareDistance(gpnts[j]);
11571                       if (d2 < distance2)
11572                         {
11573                           distance2 = d2;
11574                           if (distance2 < radius2)
11575                             break;
11576                         }
11577                     }
11578                   mapOfNodeDistance2[pts[i]] = distance2;
11579                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11580                 }
11581               if (distance2 < radius2)
11582                 {
11583                   volInside = true; // one or more nodes inside the domain
11584                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11585                   break;
11586                 }
11587             }
11588           if (volInside)
11589             {
11590               setOfInsideVol.insert(vtkId);
11591               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11592               int neighborsVtkIds[NBMAXNEIGHBORS];
11593               int downIds[NBMAXNEIGHBORS];
11594               unsigned char downTypes[NBMAXNEIGHBORS];
11595               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11596               for (int n = 0; n < nbNeighbors; n++)
11597                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11598                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11599             }
11600           else
11601             {
11602               setOfOutsideVol.insert(vtkId);
11603               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11604             }
11605           setOfVolToCheck.erase(vtkId);
11606         }
11607     }
11608
11609   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11610   //     If yes, add the volume to the inside set
11611
11612   bool addedInside = true;
11613   std::set<int> setOfVolToReCheck;
11614   while (addedInside)
11615     {
11616       MESSAGE(" --------------------------- re check");
11617       addedInside = false;
11618       std::set<int>::iterator itv = setOfInsideVol.begin();
11619       for (; itv != setOfInsideVol.end(); ++itv)
11620         {
11621           int vtkId = *itv;
11622           int neighborsVtkIds[NBMAXNEIGHBORS];
11623           int downIds[NBMAXNEIGHBORS];
11624           unsigned char downTypes[NBMAXNEIGHBORS];
11625           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11626           for (int n = 0; n < nbNeighbors; n++)
11627             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11628               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11629         }
11630       setOfVolToCheck = setOfVolToReCheck;
11631       setOfVolToReCheck.clear();
11632       while  (!setOfVolToCheck.empty())
11633         {
11634           std::set<int>::iterator it = setOfVolToCheck.begin();
11635           int vtkId = *it;
11636           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11637             {
11638               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11639               int countInside = 0;
11640               int neighborsVtkIds[NBMAXNEIGHBORS];
11641               int downIds[NBMAXNEIGHBORS];
11642               unsigned char downTypes[NBMAXNEIGHBORS];
11643               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11644               for (int n = 0; n < nbNeighbors; n++)
11645                 if (setOfInsideVol.count(neighborsVtkIds[n]))
11646                   countInside++;
11647               MESSAGE("countInside " << countInside);
11648               if (countInside > 1)
11649                 {
11650                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11651                   setOfInsideVol.insert(vtkId);
11652                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11653                   addedInside = true;
11654                 }
11655               else
11656                 setOfVolToReCheck.insert(vtkId);
11657             }
11658           setOfVolToCheck.erase(vtkId);
11659         }
11660     }
11661
11662   // --- map of Downward faces at the boundary, inside the global volume
11663   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11664   //     fill group of SMDS faces inside the volume (when several volume shapes)
11665   //     fill group of SMDS faces on the skin of the global volume (if skin)
11666
11667   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11668   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
11669   std::set<int>::iterator it = setOfInsideVol.begin();
11670   for (; it != setOfInsideVol.end(); ++it)
11671     {
11672       int vtkId = *it;
11673       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11674       int neighborsVtkIds[NBMAXNEIGHBORS];
11675       int downIds[NBMAXNEIGHBORS];
11676       unsigned char downTypes[NBMAXNEIGHBORS];
11677       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11678       for (int n = 0; n < nbNeighbors; n++)
11679         {
11680           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11681           if (neighborDim == 3)
11682             {
11683               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11684                 {
11685                   DownIdType face(downIds[n], downTypes[n]);
11686                   boundaryFaces[face] = vtkId;
11687                 }
11688               // if the face between to volumes is in the mesh, get it (internal face between shapes)
11689               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11690               if (vtkFaceId >= 0)
11691                 {
11692                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11693                   // find also the smds edges on this face
11694                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11695                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11696                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11697                   for (int i = 0; i < nbEdges; i++)
11698                     {
11699                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11700                       if (vtkEdgeId >= 0)
11701                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11702                     }
11703                 }
11704             }
11705           else if (neighborDim == 2) // skin of the volume
11706             {
11707               DownIdType face(downIds[n], downTypes[n]);
11708               skinFaces[face] = vtkId;
11709               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11710               if (vtkFaceId >= 0)
11711                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11712             }
11713         }
11714     }
11715
11716   // --- identify the edges constituting the wire of each subshape on the skin
11717   //     define polylines with the nodes of edges, equivalent to wires
11718   //     project polylines on subshapes, and partition, to get geom faces
11719
11720   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11721   std::set<int> emptySet;
11722   emptySet.clear();
11723   std::set<int> shapeIds;
11724
11725   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11726   while (itelem->more())
11727     {
11728       const SMDS_MeshElement *elem = itelem->next();
11729       int shapeId = elem->getshapeId();
11730       int vtkId = elem->getVtkId();
11731       if (!shapeIdToVtkIdSet.count(shapeId))
11732         {
11733           shapeIdToVtkIdSet[shapeId] = emptySet;
11734           shapeIds.insert(shapeId);
11735         }
11736       shapeIdToVtkIdSet[shapeId].insert(vtkId);
11737     }
11738
11739   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11740   std::set<DownIdType, DownIdCompare> emptyEdges;
11741   emptyEdges.clear();
11742
11743   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
11744   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11745     {
11746       int shapeId = itShape->first;
11747       MESSAGE(" --- Shape ID --- "<< shapeId);
11748       shapeIdToEdges[shapeId] = emptyEdges;
11749
11750       std::vector<int> nodesEdges;
11751
11752       std::set<int>::iterator its = itShape->second.begin();
11753       for (; its != itShape->second.end(); ++its)
11754         {
11755           int vtkId = *its;
11756           MESSAGE("     " << vtkId);
11757           int neighborsVtkIds[NBMAXNEIGHBORS];
11758           int downIds[NBMAXNEIGHBORS];
11759           unsigned char downTypes[NBMAXNEIGHBORS];
11760           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11761           for (int n = 0; n < nbNeighbors; n++)
11762             {
11763               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11764                 continue;
11765               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11766               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11767               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11768                 {
11769                   DownIdType edge(downIds[n], downTypes[n]);
11770                   if (!shapeIdToEdges[shapeId].count(edge))
11771                     {
11772                       shapeIdToEdges[shapeId].insert(edge);
11773                       int vtkNodeId[3];
11774                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11775                       nodesEdges.push_back(vtkNodeId[0]);
11776                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11777                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11778                     }
11779                 }
11780             }
11781         }
11782
11783       std::list<int> order;
11784       order.clear();
11785       if (nodesEdges.size() > 0)
11786         {
11787           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
11788           nodesEdges[0] = -1;
11789           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
11790           nodesEdges[1] = -1; // do not reuse this edge
11791           bool found = true;
11792           while (found)
11793             {
11794               int nodeTofind = order.back(); // try first to push back
11795               int i = 0;
11796               for (i = 0; i<nodesEdges.size(); i++)
11797                 if (nodesEdges[i] == nodeTofind)
11798                   break;
11799               if (i == nodesEdges.size())
11800                 found = false; // no follower found on back
11801               else
11802                 {
11803                   if (i%2) // odd ==> use the previous one
11804                     if (nodesEdges[i-1] < 0)
11805                       found = false;
11806                     else
11807                       {
11808                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
11809                         nodesEdges[i-1] = -1;
11810                       }
11811                   else // even ==> use the next one
11812                     if (nodesEdges[i+1] < 0)
11813                       found = false;
11814                     else
11815                       {
11816                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
11817                         nodesEdges[i+1] = -1;
11818                       }
11819                 }
11820               if (found)
11821                 continue;
11822               // try to push front
11823               found = true;
11824               nodeTofind = order.front(); // try to push front
11825               for (i = 0; i<nodesEdges.size(); i++)
11826                 if (nodesEdges[i] == nodeTofind)
11827                   break;
11828               if (i == nodesEdges.size())
11829                 {
11830                   found = false; // no predecessor found on front
11831                   continue;
11832                 }
11833               if (i%2) // odd ==> use the previous one
11834                 if (nodesEdges[i-1] < 0)
11835                   found = false;
11836                 else
11837                   {
11838                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
11839                     nodesEdges[i-1] = -1;
11840                   }
11841               else // even ==> use the next one
11842                 if (nodesEdges[i+1] < 0)
11843                   found = false;
11844                 else
11845                   {
11846                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
11847                     nodesEdges[i+1] = -1;
11848                   }
11849             }
11850         }
11851
11852
11853       std::vector<int> nodes;
11854       nodes.push_back(shapeId);
11855       std::list<int>::iterator itl = order.begin();
11856       for (; itl != order.end(); itl++)
11857         {
11858           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11859           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
11860         }
11861       listOfListOfNodes.push_back(nodes);
11862     }
11863
11864   //     partition geom faces with blocFissure
11865   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11866   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11867
11868   return;
11869 }
11870
11871
11872 //================================================================================
11873 /*!
11874  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11875  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11876  * \return TRUE if operation has been completed successfully, FALSE otherwise
11877  */
11878 //================================================================================
11879
11880 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11881 {
11882   // iterates on volume elements and detect all free faces on them
11883   SMESHDS_Mesh* aMesh = GetMeshDS();
11884   if (!aMesh)
11885     return false;
11886   //bool res = false;
11887   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11888   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11889   while(vIt->more())
11890   {
11891     const SMDS_MeshVolume* volume = vIt->next();
11892     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11893     vTool.SetExternalNormal();
11894     //const bool isPoly = volume->IsPoly();
11895     const int iQuad = volume->IsQuadratic();
11896     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11897     {
11898       if (!vTool.IsFreeFace(iface))
11899         continue;
11900       nbFree++;
11901       vector<const SMDS_MeshNode *> nodes;
11902       int nbFaceNodes = vTool.NbFaceNodes(iface);
11903       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11904       int inode = 0;
11905       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11906         nodes.push_back(faceNodes[inode]);
11907       if (iQuad) { // add medium nodes
11908         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11909           nodes.push_back(faceNodes[inode]);
11910         if ( nbFaceNodes == 9 ) // bi-quadratic quad
11911           nodes.push_back(faceNodes[8]);
11912       }
11913       // add new face based on volume nodes
11914       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11915         nbExisted++;
11916         continue; // face already exsist
11917       }
11918       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11919       nbCreated++;
11920     }
11921   }
11922   return ( nbFree==(nbExisted+nbCreated) );
11923 }
11924
11925 namespace
11926 {
11927   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11928   {
11929     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11930       return n;
11931     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11932   }
11933 }
11934 //================================================================================
11935 /*!
11936  * \brief Creates missing boundary elements
11937  *  \param elements - elements whose boundary is to be checked
11938  *  \param dimension - defines type of boundary elements to create
11939  *  \param group - a group to store created boundary elements in
11940  *  \param targetMesh - a mesh to store created boundary elements in
11941  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11942  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
11943  *                                boundary elements will be copied into the targetMesh
11944  *  \param toAddExistingBondary - if true, not only new but also pre-existing
11945  *                                boundary elements will be added into the new group
11946  *  \param aroundElements - if true, elements will be created on boundary of given
11947  *                          elements else, on boundary of the whole mesh.
11948  * \return nb of added boundary elements
11949  */
11950 //================================================================================
11951
11952 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11953                                        Bnd_Dimension           dimension,
11954                                        SMESH_Group*            group/*=0*/,
11955                                        SMESH_Mesh*             targetMesh/*=0*/,
11956                                        bool                    toCopyElements/*=false*/,
11957                                        bool                    toCopyExistingBoundary/*=false*/,
11958                                        bool                    toAddExistingBondary/*= false*/,
11959                                        bool                    aroundElements/*= false*/)
11960 {
11961   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11962   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11963   // hope that all elements are of the same type, do not check them all
11964   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11965     throw SALOME_Exception(LOCALIZED("wrong element type"));
11966
11967   if ( !targetMesh )
11968     toCopyElements = toCopyExistingBoundary = false;
11969
11970   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11971   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11972   int nbAddedBnd = 0;
11973
11974   // editor adding present bnd elements and optionally holding elements to add to the group
11975   SMESH_MeshEditor* presentEditor;
11976   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11977   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11978
11979   SMESH_MesherHelper helper( *myMesh );
11980   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11981   SMDS_VolumeTool vTool;
11982   TIDSortedElemSet avoidSet;
11983   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11984   int inode;
11985
11986   typedef vector<const SMDS_MeshNode*> TConnectivity;
11987
11988   SMDS_ElemIteratorPtr eIt;
11989   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11990   else                  eIt = elemSetIterator( elements );
11991
11992   while (eIt->more())
11993   {
11994     const SMDS_MeshElement* elem = eIt->next();
11995     const int              iQuad = elem->IsQuadratic();
11996
11997     // ------------------------------------------------------------------------------------
11998     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11999     // ------------------------------------------------------------------------------------
12000     vector<const SMDS_MeshElement*> presentBndElems;
12001     vector<TConnectivity>           missingBndElems;
12002     TConnectivity nodes, elemNodes;
12003     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12004     {
12005       vTool.SetExternalNormal();
12006       const SMDS_MeshElement* otherVol = 0;
12007       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12008       {
12009         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12010              ( !aroundElements || elements.count( otherVol )))
12011           continue;
12012         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12013         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12014         if ( missType == SMDSAbs_Edge ) // boundary edges
12015         {
12016           nodes.resize( 2+iQuad );
12017           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12018           {
12019             for ( int j = 0; j < nodes.size(); ++j )
12020               nodes[j] =nn[i+j];
12021             if ( const SMDS_MeshElement* edge =
12022                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12023               presentBndElems.push_back( edge );
12024             else
12025               missingBndElems.push_back( nodes );
12026           }
12027         }
12028         else // boundary face
12029         {
12030           nodes.clear();
12031           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12032             nodes.push_back( nn[inode] ); // add corner nodes
12033           if (iQuad)
12034             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12035               nodes.push_back( nn[inode] ); // add medium nodes
12036           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12037           if ( iCenter > 0 )
12038             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12039
12040           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12041                                                                SMDSAbs_Face, /*noMedium=*/false ))
12042             presentBndElems.push_back( f );
12043           else
12044             missingBndElems.push_back( nodes );
12045
12046           if ( targetMesh != myMesh )
12047           {
12048             // add 1D elements on face boundary to be added to a new mesh
12049             const SMDS_MeshElement* edge;
12050             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12051             {
12052               if ( iQuad )
12053                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12054               else
12055                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12056               if ( edge && avoidSet.insert( edge ).second )
12057                 presentBndElems.push_back( edge );
12058             }
12059           }
12060         }
12061       }
12062     }
12063     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12064     {
12065       avoidSet.clear(), avoidSet.insert( elem );
12066       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12067                         SMDS_MeshElement::iterator() );
12068       elemNodes.push_back( elemNodes[0] );
12069       nodes.resize( 2 + iQuad );
12070       const int nbLinks = elem->NbCornerNodes();
12071       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12072       {
12073         nodes[0] = elemNodes[iN];
12074         nodes[1] = elemNodes[iN+1+iQuad];
12075         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12076           continue; // not free link
12077
12078         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12079         if ( const SMDS_MeshElement* edge =
12080              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12081           presentBndElems.push_back( edge );
12082         else
12083           missingBndElems.push_back( nodes );
12084       }
12085     }
12086
12087     // ---------------------------------
12088     // 2. Add missing boundary elements
12089     // ---------------------------------
12090     if ( targetMesh != myMesh )
12091       // instead of making a map of nodes in this mesh and targetMesh,
12092       // we create nodes with same IDs.
12093       for ( int i = 0; i < missingBndElems.size(); ++i )
12094       {
12095         TConnectivity& srcNodes = missingBndElems[i];
12096         TConnectivity  nodes( srcNodes.size() );
12097         for ( inode = 0; inode < nodes.size(); ++inode )
12098           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12099         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12100                                                                    missType,
12101                                                                    /*noMedium=*/false))
12102           continue;
12103         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12104         ++nbAddedBnd;
12105       }
12106     else
12107       for ( int i = 0; i < missingBndElems.size(); ++i )
12108       {
12109         TConnectivity& nodes = missingBndElems[i];
12110         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12111                                                                    missType,
12112                                                                    /*noMedium=*/false))
12113           continue;
12114         SMDS_MeshElement* elem =
12115           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12116         ++nbAddedBnd;
12117
12118         // try to set a new element to a shape
12119         if ( myMesh->HasShapeToMesh() )
12120         {
12121           bool ok = true;
12122           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12123           const int nbN = nodes.size() / (iQuad+1 );
12124           for ( inode = 0; inode < nbN && ok; ++inode )
12125           {
12126             pair<int, TopAbs_ShapeEnum> i_stype =
12127               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12128             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12129               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12130           }
12131           if ( ok && mediumShapes.size() > 1 )
12132           {
12133             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12134             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12135             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12136             {
12137               if (( ok = ( stype_i->first != stype_i_0.first )))
12138                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12139                                         aMesh->IndexToShape( stype_i_0.second ));
12140             }
12141           }
12142           if ( ok && mediumShapes.begin()->first == missShapeType )
12143             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12144         }
12145       }
12146
12147     // ----------------------------------
12148     // 3. Copy present boundary elements
12149     // ----------------------------------
12150     if ( toCopyExistingBoundary )
12151       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12152       {
12153         const SMDS_MeshElement* e = presentBndElems[i];
12154         TConnectivity nodes( e->NbNodes() );
12155         for ( inode = 0; inode < nodes.size(); ++inode )
12156           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12157         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12158       }
12159     else // store present elements to add them to a group
12160       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12161       {
12162         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12163       }
12164
12165   } // loop on given elements
12166
12167   // ---------------------------------------------
12168   // 4. Fill group with boundary elements
12169   // ---------------------------------------------
12170   if ( group )
12171   {
12172     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12173       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12174         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12175   }
12176   tgtEditor.myLastCreatedElems.Clear();
12177   tgtEditor2.myLastCreatedElems.Clear();
12178
12179   // -----------------------
12180   // 5. Copy given elements
12181   // -----------------------
12182   if ( toCopyElements && targetMesh != myMesh )
12183   {
12184     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12185     else                  eIt = elemSetIterator( elements );
12186     while (eIt->more())
12187     {
12188       const SMDS_MeshElement* elem = eIt->next();
12189       TConnectivity nodes( elem->NbNodes() );
12190       for ( inode = 0; inode < nodes.size(); ++inode )
12191         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12192       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12193
12194       tgtEditor.myLastCreatedElems.Clear();
12195     }
12196   }
12197   return nbAddedBnd;
12198 }