Salome HOME
22830: EDF 9557 SMESH: Quadratic conversion of a mesh fails
[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 /*!
1248  * \brief Reorient faces basing on orientation of adjacent volumes.
1249  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1250  * \param theVolumes - reference volumes.
1251  * \param theOutsideNormal - to orient faces to have their normal
1252  *        pointing either \a outside or \a inside the adjacent volumes.
1253  * \return number of reoriented faces.
1254  */
1255 //================================================================================
1256
1257 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1258                                       TIDSortedElemSet & theVolumes,
1259                                       const bool         theOutsideNormal)
1260 {
1261   int nbReori = 0;
1262
1263   SMDS_ElemIteratorPtr faceIt;
1264   if ( theFaces.empty() )
1265     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1266   else
1267     faceIt = elemSetIterator( theFaces );
1268
1269   vector< const SMDS_MeshNode* > faceNodes;
1270   TIDSortedElemSet checkedVolumes;
1271   set< const SMDS_MeshNode* > faceNodesSet;
1272   SMDS_VolumeTool volumeTool;
1273
1274   while ( faceIt->more() ) // loop on given faces
1275   {
1276     const SMDS_MeshElement* face = faceIt->next();
1277     if ( face->GetType() != SMDSAbs_Face )
1278       continue;
1279
1280     const int nbCornersNodes = face->NbCornerNodes();
1281     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1282
1283     checkedVolumes.clear();
1284     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1285     while ( vIt->more() )
1286     {
1287       const SMDS_MeshElement* volume = vIt->next();
1288
1289       if ( !checkedVolumes.insert( volume ).second )
1290         continue;
1291       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1292         continue;
1293
1294       // is volume adjacent?
1295       bool allNodesCommon = true;
1296       for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1297         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1298       if ( !allNodesCommon )
1299         continue;
1300
1301       // get nodes of a corresponding volume facet
1302       faceNodesSet.clear();
1303       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1304       volumeTool.Set( volume );
1305       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1306       if ( facetID < 0 ) continue;
1307       volumeTool.SetExternalNormal();
1308       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1309
1310       // compare order of faceNodes and facetNodes
1311       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1312       int iNN[2];
1313       for ( int i = 0; i < 2; ++i )
1314       {
1315         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1316         for ( int iN = 0; iN < nbCornersNodes; ++iN )
1317           if ( faceNodes[ iN ] == n )
1318           {
1319             iNN[ i ] = iN;
1320             break;
1321           }
1322       }
1323       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1324       if ( isOutside != theOutsideNormal )
1325         nbReori += Reorient( face );
1326     }
1327   }  // loop on given faces
1328
1329   return nbReori;
1330 }
1331
1332 //=======================================================================
1333 //function : getBadRate
1334 //purpose  :
1335 //=======================================================================
1336
1337 static double getBadRate (const SMDS_MeshElement*               theElem,
1338                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1339 {
1340   SMESH::Controls::TSequenceOfXYZ P;
1341   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1342     return 1e100;
1343   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1344   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1345 }
1346
1347 //=======================================================================
1348 //function : QuadToTri
1349 //purpose  : Cut quadrangles into triangles.
1350 //           theCrit is used to select a diagonal to cut
1351 //=======================================================================
1352
1353 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1354                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1355 {
1356   myLastCreatedElems.Clear();
1357   myLastCreatedNodes.Clear();
1358
1359   if ( !theCrit.get() )
1360     return false;
1361
1362   SMESHDS_Mesh * aMesh = GetMeshDS();
1363
1364   Handle(Geom_Surface) surface;
1365   SMESH_MesherHelper   helper( *GetMesh() );
1366
1367   TIDSortedElemSet::iterator itElem;
1368   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1369   {
1370     const SMDS_MeshElement* elem = *itElem;
1371     if ( !elem || elem->GetType() != SMDSAbs_Face )
1372       continue;
1373     if ( elem->NbCornerNodes() != 4 )
1374       continue;
1375
1376     // retrieve element nodes
1377     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1378
1379     // compare two sets of possible triangles
1380     double aBadRate1, aBadRate2; // to what extent a set is bad
1381     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1382     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1383     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1384
1385     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1386     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1387     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1388
1389     const int aShapeId = FindShape( elem );
1390     const SMDS_MeshElement* newElem1 = 0;
1391     const SMDS_MeshElement* newElem2 = 0;
1392
1393     if ( !elem->IsQuadratic() ) // split liner quadrangle
1394     {
1395       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1396       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1397       if ( aBadRate1 <= aBadRate2 ) {
1398         // tr1 + tr2 is better
1399         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1400         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1401       }
1402       else {
1403         // tr3 + tr4 is better
1404         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1405         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1406       }
1407     }
1408     else // split quadratic quadrangle
1409     {
1410       helper.SetIsQuadratic( true );
1411       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1412
1413       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1414       if ( aNodes.size() == 9 )
1415       {
1416         helper.SetIsBiQuadratic( true );
1417         if ( aBadRate1 <= aBadRate2 )
1418           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1419         else
1420           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1421       }
1422       // create a new element
1423       if ( aBadRate1 <= aBadRate2 ) {
1424         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1425         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1426       }
1427       else {
1428         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1429         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1430       }
1431     } // quadratic case
1432
1433     // care of a new element
1434
1435     myLastCreatedElems.Append(newElem1);
1436     myLastCreatedElems.Append(newElem2);
1437     AddToSameGroups( newElem1, elem, aMesh );
1438     AddToSameGroups( newElem2, elem, aMesh );
1439
1440     // put a new triangle on the same shape
1441     if ( aShapeId )
1442       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1443     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1444
1445     aMesh->RemoveElement( elem );
1446   }
1447   return true;
1448 }
1449
1450 //=======================================================================
1451 /*!
1452  * \brief Split each of given quadrangles into 4 triangles.
1453  * \param theElems - The faces to be splitted. If empty all faces are split.
1454  */
1455 //=======================================================================
1456
1457 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1458 {
1459   myLastCreatedElems.Clear();
1460   myLastCreatedNodes.Clear();
1461
1462   SMESH_MesherHelper helper( *GetMesh() );
1463   helper.SetElementsOnShape( true );
1464
1465   SMDS_ElemIteratorPtr faceIt;
1466   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1467   else                    faceIt = elemSetIterator( theElems );
1468
1469   bool   checkUV;
1470   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1471   gp_XYZ xyz[9];
1472   vector< const SMDS_MeshNode* > nodes;
1473   SMESHDS_SubMesh*               subMeshDS;
1474   TopoDS_Face                    F;
1475   Handle(Geom_Surface)           surface;
1476   TopLoc_Location                loc;
1477
1478   while ( faceIt->more() )
1479   {
1480     const SMDS_MeshElement* quad = faceIt->next();
1481     if ( !quad || quad->NbCornerNodes() != 4 )
1482       continue;
1483
1484     // get a surface the quad is on
1485
1486     if ( quad->getshapeId() < 1 )
1487     {
1488       F.Nullify();
1489       helper.SetSubShape( 0 );
1490       subMeshDS = 0;
1491     }
1492     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1493     {
1494       helper.SetSubShape( quad->getshapeId() );
1495       if ( !helper.GetSubShape().IsNull() &&
1496            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1497       {
1498         F = TopoDS::Face( helper.GetSubShape() );
1499         surface = BRep_Tool::Surface( F, loc );
1500         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1501       }
1502       else
1503       {
1504         helper.SetSubShape( 0 );
1505         subMeshDS = 0;
1506       }
1507     }
1508
1509     // create a central node
1510
1511     const SMDS_MeshNode* nCentral;
1512     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1513
1514     if ( nodes.size() == 9 )
1515     {
1516       nCentral = nodes.back();
1517     }
1518     else
1519     {
1520       size_t iN = 0;
1521       if ( F.IsNull() )
1522       {
1523         for ( ; iN < nodes.size(); ++iN )
1524           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1525
1526         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1527           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1528
1529         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1530                                    xyz[0], xyz[1], xyz[2], xyz[3],
1531                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1532       }
1533       else
1534       {
1535         for ( ; iN < nodes.size(); ++iN )
1536           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1537
1538         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1539           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1540
1541         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1542                                   uv[0], uv[1], uv[2], uv[3],
1543                                   uv[4], uv[5], uv[6], uv[7] );
1544
1545         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1546         xyz[ 8 ] = p.XYZ();
1547       }
1548
1549       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1550                                  uv[8].X(), uv[8].Y() );
1551       myLastCreatedNodes.Append( nCentral );
1552     }
1553
1554     // create 4 triangles
1555
1556     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1557     
1558     helper.SetIsQuadratic  ( nodes.size() > 4 );
1559     helper.SetIsBiQuadratic( nodes.size() == 9 );
1560     if ( helper.GetIsQuadratic() )
1561       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1562
1563     for ( int i = 0; i < 4; ++i )
1564     {
1565       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1566                                                nodes[(i+1)%4],
1567                                                nCentral );
1568       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1569       myLastCreatedElems.Append( tria );
1570     }
1571   }
1572 }
1573
1574 //=======================================================================
1575 //function : BestSplit
1576 //purpose  : Find better diagonal for cutting.
1577 //=======================================================================
1578
1579 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1580                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1581 {
1582   myLastCreatedElems.Clear();
1583   myLastCreatedNodes.Clear();
1584
1585   if (!theCrit.get())
1586     return -1;
1587
1588   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1589     return -1;
1590
1591   if( theQuad->NbNodes()==4 ||
1592       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1593
1594     // retrieve element nodes
1595     const SMDS_MeshNode* aNodes [4];
1596     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1597     int i = 0;
1598     //while (itN->more())
1599     while (i<4) {
1600       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1601     }
1602     // compare two sets of possible triangles
1603     double aBadRate1, aBadRate2; // to what extent a set is bad
1604     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1605     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1606     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1607
1608     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1609     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1610     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1611     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1612     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1613     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1614       return 1; // diagonal 1-3
1615
1616     return 2; // diagonal 2-4
1617   }
1618   return -1;
1619 }
1620
1621 namespace
1622 {
1623   // Methods of splitting volumes into tetra
1624
1625   const int theHexTo5_1[5*4+1] =
1626     {
1627       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1628     };
1629   const int theHexTo5_2[5*4+1] =
1630     {
1631       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1632     };
1633   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1634
1635   const int theHexTo6_1[6*4+1] =
1636     {
1637       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
1638     };
1639   const int theHexTo6_2[6*4+1] =
1640     {
1641       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
1642     };
1643   const int theHexTo6_3[6*4+1] =
1644     {
1645       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
1646     };
1647   const int theHexTo6_4[6*4+1] =
1648     {
1649       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
1650     };
1651   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1652
1653   const int thePyraTo2_1[2*4+1] =
1654     {
1655       0, 1, 2, 4,    0, 2, 3, 4,   -1
1656     };
1657   const int thePyraTo2_2[2*4+1] =
1658     {
1659       1, 2, 3, 4,    1, 3, 0, 4,   -1
1660     };
1661   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1662
1663   const int thePentaTo3_1[3*4+1] =
1664     {
1665       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1666     };
1667   const int thePentaTo3_2[3*4+1] =
1668     {
1669       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1670     };
1671   const int thePentaTo3_3[3*4+1] =
1672     {
1673       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1674     };
1675   const int thePentaTo3_4[3*4+1] =
1676     {
1677       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1678     };
1679   const int thePentaTo3_5[3*4+1] =
1680     {
1681       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1682     };
1683   const int thePentaTo3_6[3*4+1] =
1684     {
1685       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1686     };
1687   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1688                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1689
1690   // Methods of splitting hexahedron into prisms
1691
1692   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1693     {
1694       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
1695     };
1696   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1697     {
1698       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
1699     };
1700   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1701     {
1702       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
1703     };
1704
1705   const int theHexTo2Prisms_BT_1[6*2+1] =
1706     {
1707       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1708     };
1709   const int theHexTo2Prisms_BT_2[6*2+1] =
1710     {
1711       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1712     };
1713   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1714
1715   const int theHexTo2Prisms_LR_1[6*2+1] =
1716     {
1717       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1718     };
1719   const int theHexTo2Prisms_LR_2[6*2+1] =
1720     {
1721       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1722     };
1723   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1724
1725   const int theHexTo2Prisms_FB_1[6*2+1] =
1726     {
1727       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1728     };
1729   const int theHexTo2Prisms_FB_2[6*2+1] =
1730     {
1731       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1732     };
1733   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1734
1735
1736   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1737   {
1738     int _n1, _n2, _n3;
1739     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1740     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1741     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1742                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1743   };
1744   struct TSplitMethod
1745   {
1746     int        _nbSplits;
1747     int        _nbCorners;
1748     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1749     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1750     bool       _ownConn;      //!< to delete _connectivity in destructor
1751     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1752
1753     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1754       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1755     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1756     bool hasFacet( const TTriangleFacet& facet ) const
1757     {
1758       if ( _nbCorners == 4 )
1759       {
1760         const int* tetConn = _connectivity;
1761         for ( ; tetConn[0] >= 0; tetConn += 4 )
1762           if (( facet.contains( tetConn[0] ) +
1763                 facet.contains( tetConn[1] ) +
1764                 facet.contains( tetConn[2] ) +
1765                 facet.contains( tetConn[3] )) == 3 )
1766             return true;
1767       }
1768       else // prism, _nbCorners == 6
1769       {
1770         const int* prismConn = _connectivity;
1771         for ( ; prismConn[0] >= 0; prismConn += 6 )
1772         {
1773           if (( facet.contains( prismConn[0] ) &&
1774                 facet.contains( prismConn[1] ) &&
1775                 facet.contains( prismConn[2] ))
1776               ||
1777               ( facet.contains( prismConn[3] ) &&
1778                 facet.contains( prismConn[4] ) &&
1779                 facet.contains( prismConn[5] )))
1780             return true;
1781         }
1782       }
1783       return false;
1784     }
1785   };
1786
1787   //=======================================================================
1788   /*!
1789    * \brief return TSplitMethod for the given element to split into tetrahedra
1790    */
1791   //=======================================================================
1792
1793   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1794   {
1795     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1796
1797     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1798     // an edge and a face barycenter; tertaherdons are based on triangles and
1799     // a volume barycenter
1800     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1801
1802     // Find out how adjacent volumes are split
1803
1804     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1805     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1806     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1807     {
1808       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1809       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1810       if ( nbNodes < 4 ) continue;
1811
1812       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1813       const int* nInd = vol.GetFaceNodesIndices( iF );
1814       if ( nbNodes == 4 )
1815       {
1816         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1817         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1818         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1819         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1820       }
1821       else
1822       {
1823         int iCom = 0; // common node of triangle faces to split into
1824         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1825         {
1826           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1827                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1828                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1829           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1830                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1831                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1832           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1833           {
1834             triaSplits.push_back( t012 );
1835             triaSplits.push_back( t023 );
1836             break;
1837           }
1838         }
1839       }
1840       if ( !triaSplits.empty() )
1841         hasAdjacentSplits = true;
1842     }
1843
1844     // Among variants of split method select one compliant with adjacent volumes
1845
1846     TSplitMethod method;
1847     if ( !vol.Element()->IsPoly() && !is24TetMode )
1848     {
1849       int nbVariants = 2, nbTet = 0;
1850       const int** connVariants = 0;
1851       switch ( vol.Element()->GetEntityType() )
1852       {
1853       case SMDSEntity_Hexa:
1854       case SMDSEntity_Quad_Hexa:
1855       case SMDSEntity_TriQuad_Hexa:
1856         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1857           connVariants = theHexTo5, nbTet = 5;
1858         else
1859           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1860         break;
1861       case SMDSEntity_Pyramid:
1862       case SMDSEntity_Quad_Pyramid:
1863         connVariants = thePyraTo2;  nbTet = 2;
1864         break;
1865       case SMDSEntity_Penta:
1866       case SMDSEntity_Quad_Penta:
1867         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1868         break;
1869       default:
1870         nbVariants = 0;
1871       }
1872       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1873       {
1874         // check method compliancy with adjacent tetras,
1875         // all found splits must be among facets of tetras described by this method
1876         method = TSplitMethod( nbTet, connVariants[variant] );
1877         if ( hasAdjacentSplits && method._nbSplits > 0 )
1878         {
1879           bool facetCreated = true;
1880           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1881           {
1882             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1883             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1884               facetCreated = method.hasFacet( *facet );
1885           }
1886           if ( !facetCreated )
1887             method = TSplitMethod(0); // incompatible method
1888         }
1889       }
1890     }
1891     if ( method._nbSplits < 1 )
1892     {
1893       // No standard method is applicable, use a generic solution:
1894       // each facet of a volume is split into triangles and
1895       // each of triangles and a volume barycenter form a tetrahedron.
1896
1897       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1898
1899       int* connectivity = new int[ maxTetConnSize + 1 ];
1900       method._connectivity = connectivity;
1901       method._ownConn = true;
1902       method._baryNode = !isHex27; // to create central node or not
1903
1904       int connSize = 0;
1905       int baryCenInd = vol.NbNodes() - int( isHex27 );
1906       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1907       {
1908         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1909         const int*   nInd = vol.GetFaceNodesIndices( iF );
1910         // find common node of triangle facets of tetra to create
1911         int iCommon = 0; // index in linear numeration
1912         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1913         if ( !triaSplits.empty() )
1914         {
1915           // by found facets
1916           const TTriangleFacet* facet = &triaSplits.front();
1917           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1918             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1919                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1920               break;
1921         }
1922         else if ( nbNodes > 3 && !is24TetMode )
1923         {
1924           // find the best method of splitting into triangles by aspect ratio
1925           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1926           map< double, int > badness2iCommon;
1927           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1928           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1929           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1930           {
1931             double badness = 0;
1932             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1933             {
1934               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1935                                       nodes[ iQ*((iLast-1)%nbNodes)],
1936                                       nodes[ iQ*((iLast  )%nbNodes)]);
1937               badness += getBadRate( &tria, aspectRatio );
1938             }
1939             badness2iCommon.insert( make_pair( badness, iCommon ));
1940           }
1941           // use iCommon with lowest badness
1942           iCommon = badness2iCommon.begin()->second;
1943         }
1944         if ( iCommon >= nbNodes )
1945           iCommon = 0; // something wrong
1946
1947         // fill connectivity of tetrahedra based on a current face
1948         int nbTet = nbNodes - 2;
1949         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1950         {
1951           int faceBaryCenInd;
1952           if ( isHex27 )
1953           {
1954             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1955             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1956           }
1957           else
1958           {
1959             method._faceBaryNode[ iF ] = 0;
1960             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1961           }
1962           nbTet = nbNodes;
1963           for ( int i = 0; i < nbTet; ++i )
1964           {
1965             int i1 = i, i2 = (i+1) % nbNodes;
1966             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1967             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1968             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1969             connectivity[ connSize++ ] = faceBaryCenInd;
1970             connectivity[ connSize++ ] = baryCenInd;
1971           }
1972         }
1973         else
1974         {
1975           for ( int i = 0; i < nbTet; ++i )
1976           {
1977             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1978             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1979             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1980             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1981             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1982             connectivity[ connSize++ ] = baryCenInd;
1983           }
1984         }
1985         method._nbSplits += nbTet;
1986
1987       } // loop on volume faces
1988
1989       connectivity[ connSize++ ] = -1;
1990
1991     } // end of generic solution
1992
1993     return method;
1994   }
1995   //=======================================================================
1996   /*!
1997    * \brief return TSplitMethod to split haxhedron into prisms
1998    */
1999   //=======================================================================
2000
2001   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2002                                     const int        methodFlags,
2003                                     const int        facetToSplit)
2004   {
2005     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2006     // B, T, L, B, R, F
2007     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2008
2009     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2010     {
2011       static TSplitMethod to4methods[4]; // order BT, LR, FB
2012       if ( to4methods[iF]._nbSplits == 0 )
2013       {
2014         switch ( iF ) {
2015         case 0:
2016           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2017           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2018           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2019           break;
2020         case 1:
2021           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2022           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2023           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2024           break;
2025         case 2:
2026           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2027           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2028           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2029           break;
2030         default: return to4methods[3];
2031         }
2032         to4methods[iF]._nbSplits  = 4;
2033         to4methods[iF]._nbCorners = 6;
2034       }
2035       return to4methods[iF];
2036     }
2037     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2038
2039     TSplitMethod method;
2040
2041     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2042
2043     const int nbVariants = 2, nbSplits = 2;
2044     const int** connVariants = 0;
2045     switch ( iF ) {
2046     case 0: connVariants = theHexTo2Prisms_BT; break;
2047     case 1: connVariants = theHexTo2Prisms_LR; break;
2048     case 2: connVariants = theHexTo2Prisms_FB; break;
2049     default: return method;
2050     }
2051
2052     // look for prisms adjacent via facetToSplit and an opposite one
2053     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2054     {
2055       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2056       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2057       if ( nbNodes != 4 ) return method;
2058
2059       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2060       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2061       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2062       TTriangleFacet* t;
2063       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2064         t = &t012;
2065       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2066         t = &t123;
2067       else
2068         continue;
2069
2070       // there are adjacent prism
2071       for ( int variant = 0; variant < nbVariants; ++variant )
2072       {
2073         // check method compliancy with adjacent prisms,
2074         // the found prism facets must be among facets of prisms described by current method
2075         method._nbSplits     = nbSplits;
2076         method._nbCorners    = 6;
2077         method._connectivity = connVariants[ variant ];
2078         if ( method.hasFacet( *t ))
2079           return method;
2080       }
2081     }
2082
2083     // No adjacent prisms. Select a variant with a best aspect ratio.
2084
2085     double badness[2] = { 0, 0 };
2086     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2087     const SMDS_MeshNode** nodes = vol.GetNodes();
2088     for ( int variant = 0; variant < nbVariants; ++variant )
2089       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2090       {
2091         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2092         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2093
2094         method._connectivity = connVariants[ variant ];
2095         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2096         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2097         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2098
2099         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2100                                 nodes[ t->_n2 ],
2101                                 nodes[ t->_n3 ] );
2102         badness[ variant ] += getBadRate( &tria, aspectRatio );
2103       }
2104     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2105
2106     method._nbSplits     = nbSplits;
2107     method._nbCorners    = 6;
2108     method._connectivity = connVariants[ iBetter ];
2109
2110     return method;
2111   }
2112
2113   //================================================================================
2114   /*!
2115    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2116    */
2117   //================================================================================
2118
2119   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2120                                        const SMDSAbs_GeometryType geom ) const
2121   {
2122     // find the tetrahedron including the three nodes of facet
2123     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2124     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2125     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2126     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2127     while ( volIt1->more() )
2128     {
2129       const SMDS_MeshElement* v = volIt1->next();
2130       if ( v->GetGeomType() != geom )
2131         continue;
2132       const int lastCornerInd = v->NbCornerNodes() - 1;
2133       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2134         continue; // medium node not allowed
2135       const int ind2 = v->GetNodeIndex( n2 );
2136       if ( ind2 < 0 || lastCornerInd < ind2 )
2137         continue;
2138       const int ind3 = v->GetNodeIndex( n3 );
2139       if ( ind3 < 0 || lastCornerInd < ind3 )
2140         continue;
2141       return true;
2142     }
2143     return false;
2144   }
2145
2146   //=======================================================================
2147   /*!
2148    * \brief A key of a face of volume
2149    */
2150   //=======================================================================
2151
2152   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2153   {
2154     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2155     {
2156       TIDSortedNodeSet sortedNodes;
2157       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2158       int nbNodes = vol.NbFaceNodes( iF );
2159       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2160       for ( int i = 0; i < nbNodes; i += iQ )
2161         sortedNodes.insert( fNodes[i] );
2162       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2163       first.first   = (*(n++))->GetID();
2164       first.second  = (*(n++))->GetID();
2165       second.first  = (*(n++))->GetID();
2166       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2167     }
2168   };
2169 } // namespace
2170
2171 //=======================================================================
2172 //function : SplitVolumes
2173 //purpose  : Split volume elements into tetrahedra or prisms.
2174 //           If facet ID < 0, element is split into tetrahedra,
2175 //           else a hexahedron is split into prisms so that the given facet is
2176 //           split into triangles
2177 //=======================================================================
2178
2179 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2180                                      const int            theMethodFlags)
2181 {
2182   // std-like iterator on coordinates of nodes of mesh element
2183   typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2184   NXyzIterator xyzEnd;
2185
2186   SMDS_VolumeTool    volTool;
2187   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2188   fHelper.ToFixNodeParameters( true );
2189
2190   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2191   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2192
2193   SMESH_SequenceOfElemPtr newNodes, newElems;
2194
2195   // map face of volume to it's baricenrtic node
2196   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2197   double bc[3];
2198
2199   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2200   for ( ; elem2facet != theElems.end(); ++elem2facet )
2201   {
2202     const SMDS_MeshElement* elem = elem2facet->first;
2203     const int       facetToSplit = elem2facet->second;
2204     if ( elem->GetType() != SMDSAbs_Volume )
2205       continue;
2206     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2207     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2208       continue;
2209
2210     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2211
2212     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2213                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2214                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2215     if ( splitMethod._nbSplits < 1 ) continue;
2216
2217     // find submesh to add new tetras to
2218     if ( !subMesh || !subMesh->Contains( elem ))
2219     {
2220       int shapeID = FindShape( elem );
2221       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2222       subMesh = GetMeshDS()->MeshElements( shapeID );
2223     }
2224     int iQ;
2225     if ( elem->IsQuadratic() )
2226     {
2227       iQ = 2;
2228       // add quadratic links to the helper
2229       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2230       {
2231         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2232         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2233         for ( int iN = 0; iN < nbN; iN += iQ )
2234           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2235       }
2236       helper.SetIsQuadratic( true );
2237     }
2238     else
2239     {
2240       iQ = 1;
2241       helper.SetIsQuadratic( false );
2242     }
2243     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2244                                         volTool.GetNodes() + elem->NbNodes() );
2245     helper.SetElementsOnShape( true );
2246     if ( splitMethod._baryNode )
2247     {
2248       // make a node at barycenter
2249       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2250       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2251       nodes.push_back( gcNode );
2252       newNodes.Append( gcNode );
2253     }
2254     if ( !splitMethod._faceBaryNode.empty() )
2255     {
2256       // make or find baricentric nodes of faces
2257       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2258       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2259       {
2260         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2261           volFace2BaryNode.insert
2262           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2263         if ( !f_n->second )
2264         {
2265           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2266           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2267         }
2268         nodes.push_back( iF_n->second = f_n->second );
2269       }
2270     }
2271
2272     // make new volumes
2273     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2274     const int* volConn = splitMethod._connectivity;
2275     if ( splitMethod._nbCorners == 4 ) // tetra
2276       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2277         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2278                                                             nodes[ volConn[1] ],
2279                                                             nodes[ volConn[2] ],
2280                                                             nodes[ volConn[3] ]));
2281     else // prisms
2282       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2283         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2284                                                             nodes[ volConn[1] ],
2285                                                             nodes[ volConn[2] ],
2286                                                             nodes[ volConn[3] ],
2287                                                             nodes[ volConn[4] ],
2288                                                             nodes[ volConn[5] ]));
2289
2290     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2291
2292     // Split faces on sides of the split volume
2293
2294     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2295     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2296     {
2297       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2298       if ( nbNodes < 4 ) continue;
2299
2300       // find an existing face
2301       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2302                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2303       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2304                                                                        /*noMedium=*/false))
2305       {
2306         // make triangles
2307         helper.SetElementsOnShape( false );
2308         vector< const SMDS_MeshElement* > triangles;
2309
2310         // find submesh to add new triangles in
2311         if ( !fSubMesh || !fSubMesh->Contains( face ))
2312         {
2313           int shapeID = FindShape( face );
2314           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2315         }
2316         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2317         if ( iF_n != splitMethod._faceBaryNode.end() )
2318         {
2319           const SMDS_MeshNode *baryNode = iF_n->second;
2320           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2321           {
2322             const SMDS_MeshNode* n1 = fNodes[iN];
2323             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2324             const SMDS_MeshNode *n3 = baryNode;
2325             if ( !volTool.IsFaceExternal( iF ))
2326               swap( n2, n3 );
2327             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2328           }
2329           if ( fSubMesh ) // update position of the bary node on geometry
2330           {
2331             if ( subMesh )
2332               subMesh->RemoveNode( baryNode, false );
2333             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2334             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2335             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2336             {
2337               fHelper.SetSubShape( s );
2338               gp_XY uv( 1e100, 1e100 );
2339               double distXYZ[4];
2340               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2341                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2342                    uv.X() < 1e100 )
2343               {
2344                 // node is too far from the surface
2345                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2346                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2347                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2348               }
2349             }
2350           }
2351         }
2352         else
2353         {
2354           // among possible triangles create ones discribed by split method
2355           const int* nInd = volTool.GetFaceNodesIndices( iF );
2356           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2357           int iCom = 0; // common node of triangle faces to split into
2358           list< TTriangleFacet > facets;
2359           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2360           {
2361             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2362                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2363                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2364             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2365                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2366                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2367             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2368             {
2369               facets.push_back( t012 );
2370               facets.push_back( t023 );
2371               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2372                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2373                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2374                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2375               break;
2376             }
2377           }
2378           list< TTriangleFacet >::iterator facet = facets.begin();
2379           if ( facet == facets.end() )
2380             break;
2381           for ( ; facet != facets.end(); ++facet )
2382           {
2383             if ( !volTool.IsFaceExternal( iF ))
2384               swap( facet->_n2, facet->_n3 );
2385             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2386                                                  volNodes[ facet->_n2 ],
2387                                                  volNodes[ facet->_n3 ]));
2388           }
2389         }
2390         for ( int i = 0; i < triangles.size(); ++i )
2391         {
2392           if ( !triangles[i] ) continue;
2393           if ( fSubMesh )
2394             fSubMesh->AddElement( triangles[i]);
2395           newElems.Append( triangles[i] );
2396         }
2397         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2398         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2399
2400       } // while a face based on facet nodes exists
2401     } // loop on volume faces to split them into triangles
2402
2403     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2404
2405     if ( geomType == SMDSEntity_TriQuad_Hexa )
2406     {
2407       // remove medium nodes that could become free
2408       for ( int i = 20; i < volTool.NbNodes(); ++i )
2409         if ( volNodes[i]->NbInverseElements() == 0 )
2410           GetMeshDS()->RemoveNode( volNodes[i] );
2411     }
2412   } // loop on volumes to split
2413   
2414   myLastCreatedNodes = newNodes;
2415   myLastCreatedElems = newElems;
2416 }
2417
2418 //=======================================================================
2419 //function : GetHexaFacetsToSplit
2420 //purpose  : For hexahedra that will be split into prisms, finds facets to
2421 //           split into triangles. Only hexahedra adjacent to the one closest
2422 //           to theFacetNormal.Location() are returned.
2423 //param [in,out] theHexas - the hexahedra
2424 //param [in]     theFacetNormal - facet normal
2425 //param [out]    theFacets - the hexahedra and found facet IDs
2426 //=======================================================================
2427
2428 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2429                                              const gp_Ax1&     theFacetNormal,
2430                                              TFacetOfElem &    theFacets)
2431 {
2432   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2433
2434   // Find a hexa closest to the location of theFacetNormal
2435
2436   const SMDS_MeshElement* startHex;
2437   {
2438     // get SMDS_ElemIteratorPtr on theHexas
2439     typedef const SMDS_MeshElement*                                      TValue;
2440     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2441     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2442     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2443     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2444     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2445       ( new TElemSetIter( theHexas.begin(),
2446                           theHexas.end(),
2447                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2448
2449     SMESH_ElementSearcher* searcher =
2450       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2451
2452     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2453
2454     delete searcher;
2455
2456     if ( !startHex )
2457       throw SALOME_Exception( THIS_METHOD "startHex not found");
2458   }
2459
2460   // Select a facet of startHex by theFacetNormal
2461
2462   SMDS_VolumeTool vTool( startHex );
2463   double norm[3], dot, maxDot = 0;
2464   int facetID = -1;
2465   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2466     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2467     {
2468       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2469       if ( dot > maxDot )
2470       {
2471         facetID = iF;
2472         maxDot = dot;
2473       }
2474     }
2475   if ( facetID < 0 )
2476     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2477
2478   // Fill theFacets starting from facetID of startHex
2479
2480   // facets used for seach of volumes adjacent to already treated ones
2481   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2482   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2483   TFacetMap facetsToCheck;
2484
2485   set<const SMDS_MeshNode*> facetNodes;
2486   const SMDS_MeshElement*   curHex;
2487
2488   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2489
2490   while ( startHex )
2491   {
2492     // move in two directions from startHex via facetID
2493     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2494     {
2495       curHex       = startHex;
2496       int curFacet = facetID;
2497       if ( is2nd ) // do not treat startHex twice
2498       {
2499         vTool.Set( curHex );
2500         if ( vTool.IsFreeFace( curFacet, &curHex ))
2501         {
2502           curHex = 0;
2503         }
2504         else
2505         {
2506           vTool.GetFaceNodes( curFacet, facetNodes );
2507           vTool.Set( curHex );
2508           curFacet = vTool.GetFaceIndex( facetNodes );
2509         }
2510       }
2511       while ( curHex )
2512       {
2513         // store a facet to split
2514         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2515         {
2516           theFacets.insert( make_pair( curHex, -1 ));
2517           break;
2518         }
2519         if ( !allHex && !theHexas.count( curHex ))
2520           break;
2521
2522         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2523           theFacets.insert( make_pair( curHex, curFacet ));
2524         if ( !facetIt2isNew.second )
2525           break;
2526
2527         // remember not-to-split facets in facetsToCheck
2528         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2529         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2530         {
2531           if ( iF == curFacet && iF == oppFacet )
2532             continue;
2533           TVolumeFaceKey facetKey ( vTool, iF );
2534           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2535           pair< TFacetMap::iterator, bool > it2isnew =
2536             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2537           if ( !it2isnew.second )
2538             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2539         }
2540         // pass to a volume adjacent via oppFacet
2541         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2542         {
2543           curHex = 0;
2544         }
2545         else
2546         {
2547           // get a new curFacet
2548           vTool.GetFaceNodes( oppFacet, facetNodes );
2549           vTool.Set( curHex );
2550           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2551         }
2552       }
2553     } // move in two directions from startHex via facetID
2554
2555     // Find a new startHex by facetsToCheck
2556
2557     startHex = 0;
2558     facetID  = -1;
2559     TFacetMap::iterator fIt = facetsToCheck.begin();
2560     while ( !startHex && fIt != facetsToCheck.end() )
2561     {
2562       const TElemFacets&  elemFacets = fIt->second;
2563       const SMDS_MeshElement*    hex = elemFacets.first->first;
2564       int                 splitFacet = elemFacets.first->second;
2565       int               lateralFacet = elemFacets.second;
2566       facetsToCheck.erase( fIt );
2567       fIt = facetsToCheck.begin();
2568
2569       vTool.Set( hex );
2570       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2571            curHex->GetGeomType() != SMDSGeom_HEXA )
2572         continue;
2573       if ( !allHex && !theHexas.count( curHex ))
2574         continue;
2575
2576       startHex = curHex;
2577
2578       // find a facet of startHex to split 
2579
2580       set<const SMDS_MeshNode*> lateralNodes;
2581       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2582       vTool.GetFaceNodes( splitFacet,   facetNodes );
2583       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2584       vTool.Set( startHex );
2585       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2586
2587       // look for a facet of startHex having common nodes with facetNodes
2588       // but not lateralFacet
2589       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2590       {
2591         if ( iF == lateralFacet )
2592           continue;
2593         int nbCommonNodes = 0;
2594         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2595         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2596           nbCommonNodes += facetNodes.count( nn[ iN ]);
2597
2598         if ( nbCommonNodes >= 2 )
2599         {
2600           facetID = iF;
2601           break;
2602         }
2603       }
2604       if ( facetID < 0 )
2605         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2606     }
2607   } //   while ( startHex )
2608 }
2609
2610 //=======================================================================
2611 //function : AddToSameGroups
2612 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2613 //=======================================================================
2614
2615 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2616                                         const SMDS_MeshElement* elemInGroups,
2617                                         SMESHDS_Mesh *          aMesh)
2618 {
2619   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2620   if (!groups.empty()) {
2621     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2622     for ( ; grIt != groups.end(); grIt++ ) {
2623       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2624       if ( group && group->Contains( elemInGroups ))
2625         group->SMDSGroup().Add( elemToAdd );
2626     }
2627   }
2628 }
2629
2630
2631 //=======================================================================
2632 //function : RemoveElemFromGroups
2633 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2634 //=======================================================================
2635 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2636                                              SMESHDS_Mesh *          aMesh)
2637 {
2638   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2639   if (!groups.empty())
2640   {
2641     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2642     for (; GrIt != groups.end(); GrIt++)
2643     {
2644       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2645       if (!grp || grp->IsEmpty()) continue;
2646       grp->SMDSGroup().Remove(removeelem);
2647     }
2648   }
2649 }
2650
2651 //================================================================================
2652 /*!
2653  * \brief Replace elemToRm by elemToAdd in the all groups
2654  */
2655 //================================================================================
2656
2657 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2658                                             const SMDS_MeshElement* elemToAdd,
2659                                             SMESHDS_Mesh *          aMesh)
2660 {
2661   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2662   if (!groups.empty()) {
2663     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2664     for ( ; grIt != groups.end(); grIt++ ) {
2665       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2666       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2667         group->SMDSGroup().Add( elemToAdd );
2668     }
2669   }
2670 }
2671
2672 //================================================================================
2673 /*!
2674  * \brief Replace elemToRm by elemToAdd in the all groups
2675  */
2676 //================================================================================
2677
2678 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2679                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2680                                             SMESHDS_Mesh *                         aMesh)
2681 {
2682   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2683   if (!groups.empty())
2684   {
2685     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2686     for ( ; grIt != groups.end(); grIt++ ) {
2687       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2688       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2689         for ( int i = 0; i < elemToAdd.size(); ++i )
2690           group->SMDSGroup().Add( elemToAdd[ i ] );
2691     }
2692   }
2693 }
2694
2695 //=======================================================================
2696 //function : QuadToTri
2697 //purpose  : Cut quadrangles into triangles.
2698 //           theCrit is used to select a diagonal to cut
2699 //=======================================================================
2700
2701 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2702                                   const bool         the13Diag)
2703 {
2704   myLastCreatedElems.Clear();
2705   myLastCreatedNodes.Clear();
2706
2707   MESSAGE( "::QuadToTri()" );
2708
2709   SMESHDS_Mesh * aMesh = GetMeshDS();
2710
2711   Handle(Geom_Surface) surface;
2712   SMESH_MesherHelper   helper( *GetMesh() );
2713
2714   TIDSortedElemSet::iterator itElem;
2715   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2716     const SMDS_MeshElement* elem = *itElem;
2717     if ( !elem || elem->GetType() != SMDSAbs_Face )
2718       continue;
2719     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2720     if(!isquad) continue;
2721
2722     if(elem->NbNodes()==4) {
2723       // retrieve element nodes
2724       const SMDS_MeshNode* aNodes [4];
2725       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2726       int i = 0;
2727       while ( itN->more() )
2728         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2729
2730       int aShapeId = FindShape( elem );
2731       const SMDS_MeshElement* newElem1 = 0;
2732       const SMDS_MeshElement* newElem2 = 0;
2733       if ( the13Diag ) {
2734         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2735         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2736       }
2737       else {
2738         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2739         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2740       }
2741       myLastCreatedElems.Append(newElem1);
2742       myLastCreatedElems.Append(newElem2);
2743       // put a new triangle on the same shape and add to the same groups
2744       if ( aShapeId )
2745         {
2746           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2747           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2748         }
2749       AddToSameGroups( newElem1, elem, aMesh );
2750       AddToSameGroups( newElem2, elem, aMesh );
2751       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2752       aMesh->RemoveElement( elem );
2753     }
2754
2755     // Quadratic quadrangle
2756
2757     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2758
2759       // get surface elem is on
2760       int aShapeId = FindShape( elem );
2761       if ( aShapeId != helper.GetSubShapeID() ) {
2762         surface.Nullify();
2763         TopoDS_Shape shape;
2764         if ( aShapeId > 0 )
2765           shape = aMesh->IndexToShape( aShapeId );
2766         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2767           TopoDS_Face face = TopoDS::Face( shape );
2768           surface = BRep_Tool::Surface( face );
2769           if ( !surface.IsNull() )
2770             helper.SetSubShape( shape );
2771         }
2772       }
2773
2774       const SMDS_MeshNode* aNodes [8];
2775       const SMDS_MeshNode* inFaceNode = 0;
2776       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2777       int i = 0;
2778       while ( itN->more() ) {
2779         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2780         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2781              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2782         {
2783           inFaceNode = aNodes[ i-1 ];
2784         }
2785       }
2786
2787       // find middle point for (0,1,2,3)
2788       // and create a node in this point;
2789       gp_XYZ p( 0,0,0 );
2790       if ( surface.IsNull() ) {
2791         for(i=0; i<4; i++)
2792           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2793         p /= 4;
2794       }
2795       else {
2796         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2797         gp_XY uv( 0,0 );
2798         for(i=0; i<4; i++)
2799           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2800         uv /= 4.;
2801         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2802       }
2803       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2804       myLastCreatedNodes.Append(newN);
2805
2806       // create a new element
2807       const SMDS_MeshElement* newElem1 = 0;
2808       const SMDS_MeshElement* newElem2 = 0;
2809       if ( the13Diag ) {
2810         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2811                                   aNodes[6], aNodes[7], newN );
2812         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2813                                   newN,      aNodes[4], aNodes[5] );
2814       }
2815       else {
2816         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2817                                   aNodes[7], aNodes[4], newN );
2818         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2819                                   newN,      aNodes[5], aNodes[6] );
2820       }
2821       myLastCreatedElems.Append(newElem1);
2822       myLastCreatedElems.Append(newElem2);
2823       // put a new triangle on the same shape and add to the same groups
2824       if ( aShapeId )
2825         {
2826           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2827           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2828         }
2829       AddToSameGroups( newElem1, elem, aMesh );
2830       AddToSameGroups( newElem2, elem, aMesh );
2831       aMesh->RemoveElement( elem );
2832     }
2833   }
2834
2835   return true;
2836 }
2837
2838 //=======================================================================
2839 //function : getAngle
2840 //purpose  :
2841 //=======================================================================
2842
2843 double getAngle(const SMDS_MeshElement * tr1,
2844                 const SMDS_MeshElement * tr2,
2845                 const SMDS_MeshNode *    n1,
2846                 const SMDS_MeshNode *    n2)
2847 {
2848   double angle = 2. * M_PI; // bad angle
2849
2850   // get normals
2851   SMESH::Controls::TSequenceOfXYZ P1, P2;
2852   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2853        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2854     return angle;
2855   gp_Vec N1,N2;
2856   if(!tr1->IsQuadratic())
2857     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2858   else
2859     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2860   if ( N1.SquareMagnitude() <= gp::Resolution() )
2861     return angle;
2862   if(!tr2->IsQuadratic())
2863     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2864   else
2865     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2866   if ( N2.SquareMagnitude() <= gp::Resolution() )
2867     return angle;
2868
2869   // find the first diagonal node n1 in the triangles:
2870   // take in account a diagonal link orientation
2871   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2872   for ( int t = 0; t < 2; t++ ) {
2873     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2874     int i = 0, iDiag = -1;
2875     while ( it->more()) {
2876       const SMDS_MeshElement *n = it->next();
2877       if ( n == n1 || n == n2 ) {
2878         if ( iDiag < 0)
2879           iDiag = i;
2880         else {
2881           if ( i - iDiag == 1 )
2882             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2883           else
2884             nFirst[ t ] = n;
2885           break;
2886         }
2887       }
2888       i++;
2889     }
2890   }
2891   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2892     N2.Reverse();
2893
2894   angle = N1.Angle( N2 );
2895   //SCRUTE( angle );
2896   return angle;
2897 }
2898
2899 // =================================================
2900 // class generating a unique ID for a pair of nodes
2901 // and able to return nodes by that ID
2902 // =================================================
2903 class LinkID_Gen {
2904 public:
2905
2906   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2907     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2908   {}
2909
2910   long GetLinkID (const SMDS_MeshNode * n1,
2911                   const SMDS_MeshNode * n2) const
2912   {
2913     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2914   }
2915
2916   bool GetNodes (const long             theLinkID,
2917                  const SMDS_MeshNode* & theNode1,
2918                  const SMDS_MeshNode* & theNode2) const
2919   {
2920     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2921     if ( !theNode1 ) return false;
2922     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2923     if ( !theNode2 ) return false;
2924     return true;
2925   }
2926
2927 private:
2928   LinkID_Gen();
2929   const SMESHDS_Mesh* myMesh;
2930   long                myMaxID;
2931 };
2932
2933
2934 //=======================================================================
2935 //function : TriToQuad
2936 //purpose  : Fuse neighbour triangles into quadrangles.
2937 //           theCrit is used to select a neighbour to fuse with.
2938 //           theMaxAngle is a max angle between element normals at which
2939 //           fusion is still performed.
2940 //=======================================================================
2941
2942 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2943                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2944                                   const double                         theMaxAngle)
2945 {
2946   myLastCreatedElems.Clear();
2947   myLastCreatedNodes.Clear();
2948
2949   MESSAGE( "::TriToQuad()" );
2950
2951   if ( !theCrit.get() )
2952     return false;
2953
2954   SMESHDS_Mesh * aMesh = GetMeshDS();
2955
2956   // Prepare data for algo: build
2957   // 1. map of elements with their linkIDs
2958   // 2. map of linkIDs with their elements
2959
2960   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2961   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2962   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2963   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2964
2965   TIDSortedElemSet::iterator itElem;
2966   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2967   {
2968     const SMDS_MeshElement* elem = *itElem;
2969     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2970     bool IsTria = ( elem->NbCornerNodes()==3 );
2971     if (!IsTria) continue;
2972
2973     // retrieve element nodes
2974     const SMDS_MeshNode* aNodes [4];
2975     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2976     int i = 0;
2977     while ( i < 3 )
2978       aNodes[ i++ ] = itN->next();
2979     aNodes[ 3 ] = aNodes[ 0 ];
2980
2981     // fill maps
2982     for ( i = 0; i < 3; i++ ) {
2983       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2984       // check if elements sharing a link can be fused
2985       itLE = mapLi_listEl.find( link );
2986       if ( itLE != mapLi_listEl.end() ) {
2987         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2988           continue;
2989         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2990         //if ( FindShape( elem ) != FindShape( elem2 ))
2991         //  continue; // do not fuse triangles laying on different shapes
2992         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2993           continue; // avoid making badly shaped quads
2994         (*itLE).second.push_back( elem );
2995       }
2996       else {
2997         mapLi_listEl[ link ].push_back( elem );
2998       }
2999       mapEl_setLi [ elem ].insert( link );
3000     }
3001   }
3002   // Clean the maps from the links shared by a sole element, ie
3003   // links to which only one element is bound in mapLi_listEl
3004
3005   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3006     int nbElems = (*itLE).second.size();
3007     if ( nbElems < 2  ) {
3008       const SMDS_MeshElement* elem = (*itLE).second.front();
3009       SMESH_TLink link = (*itLE).first;
3010       mapEl_setLi[ elem ].erase( link );
3011       if ( mapEl_setLi[ elem ].empty() )
3012         mapEl_setLi.erase( elem );
3013     }
3014   }
3015
3016   // Algo: fuse triangles into quadrangles
3017
3018   while ( ! mapEl_setLi.empty() ) {
3019     // Look for the start element:
3020     // the element having the least nb of shared links
3021     const SMDS_MeshElement* startElem = 0;
3022     int minNbLinks = 4;
3023     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3024       int nbLinks = (*itEL).second.size();
3025       if ( nbLinks < minNbLinks ) {
3026         startElem = (*itEL).first;
3027         minNbLinks = nbLinks;
3028         if ( minNbLinks == 1 )
3029           break;
3030       }
3031     }
3032
3033     // search elements to fuse starting from startElem or links of elements
3034     // fused earlyer - startLinks
3035     list< SMESH_TLink > startLinks;
3036     while ( startElem || !startLinks.empty() ) {
3037       while ( !startElem && !startLinks.empty() ) {
3038         // Get an element to start, by a link
3039         SMESH_TLink linkId = startLinks.front();
3040         startLinks.pop_front();
3041         itLE = mapLi_listEl.find( linkId );
3042         if ( itLE != mapLi_listEl.end() ) {
3043           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3044           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3045           for ( ; itE != listElem.end() ; itE++ )
3046             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3047               startElem = (*itE);
3048           mapLi_listEl.erase( itLE );
3049         }
3050       }
3051
3052       if ( startElem ) {
3053         // Get candidates to be fused
3054         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3055         const SMESH_TLink *link12, *link13;
3056         startElem = 0;
3057         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3058         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3059         ASSERT( !setLi.empty() );
3060         set< SMESH_TLink >::iterator itLi;
3061         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3062         {
3063           const SMESH_TLink & link = (*itLi);
3064           itLE = mapLi_listEl.find( link );
3065           if ( itLE == mapLi_listEl.end() )
3066             continue;
3067
3068           const SMDS_MeshElement* elem = (*itLE).second.front();
3069           if ( elem == tr1 )
3070             elem = (*itLE).second.back();
3071           mapLi_listEl.erase( itLE );
3072           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3073             continue;
3074           if ( tr2 ) {
3075             tr3 = elem;
3076             link13 = &link;
3077           }
3078           else {
3079             tr2 = elem;
3080             link12 = &link;
3081           }
3082
3083           // add other links of elem to list of links to re-start from
3084           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3085           set< SMESH_TLink >::iterator it;
3086           for ( it = links.begin(); it != links.end(); it++ ) {
3087             const SMESH_TLink& link2 = (*it);
3088             if ( link2 != link )
3089               startLinks.push_back( link2 );
3090           }
3091         }
3092
3093         // Get nodes of possible quadrangles
3094         const SMDS_MeshNode *n12 [4], *n13 [4];
3095         bool Ok12 = false, Ok13 = false;
3096         const SMDS_MeshNode *linkNode1, *linkNode2;
3097         if(tr2) {
3098           linkNode1 = link12->first;
3099           linkNode2 = link12->second;
3100           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3101             Ok12 = true;
3102         }
3103         if(tr3) {
3104           linkNode1 = link13->first;
3105           linkNode2 = link13->second;
3106           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3107             Ok13 = true;
3108         }
3109
3110         // Choose a pair to fuse
3111         if ( Ok12 && Ok13 ) {
3112           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3113           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3114           double aBadRate12 = getBadRate( &quad12, theCrit );
3115           double aBadRate13 = getBadRate( &quad13, theCrit );
3116           if (  aBadRate13 < aBadRate12 )
3117             Ok12 = false;
3118           else
3119             Ok13 = false;
3120         }
3121
3122         // Make quadrangles
3123         // and remove fused elems and remove links from the maps
3124         mapEl_setLi.erase( tr1 );
3125         if ( Ok12 )
3126         {
3127           mapEl_setLi.erase( tr2 );
3128           mapLi_listEl.erase( *link12 );
3129           if ( tr1->NbNodes() == 3 )
3130           {
3131             const SMDS_MeshElement* newElem = 0;
3132             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3133             myLastCreatedElems.Append(newElem);
3134             AddToSameGroups( newElem, tr1, aMesh );
3135             int aShapeId = tr1->getshapeId();
3136             if ( aShapeId )
3137               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3138             aMesh->RemoveElement( tr1 );
3139             aMesh->RemoveElement( tr2 );
3140           }
3141           else {
3142             vector< const SMDS_MeshNode* > N1;
3143             vector< const SMDS_MeshNode* > N2;
3144             getNodesFromTwoTria(tr1,tr2,N1,N2);
3145             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3146             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3147             // i.e. first nodes from both arrays form a new diagonal
3148             const SMDS_MeshNode* aNodes[8];
3149             aNodes[0] = N1[0];
3150             aNodes[1] = N1[1];
3151             aNodes[2] = N2[0];
3152             aNodes[3] = N2[1];
3153             aNodes[4] = N1[3];
3154             aNodes[5] = N2[5];
3155             aNodes[6] = N2[3];
3156             aNodes[7] = N1[5];
3157             const SMDS_MeshElement* newElem = 0;
3158             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3159               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3160                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3161             else
3162               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3163                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3164             myLastCreatedElems.Append(newElem);
3165             AddToSameGroups( newElem, tr1, aMesh );
3166             int aShapeId = tr1->getshapeId();
3167             if ( aShapeId )
3168               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3169             aMesh->RemoveElement( tr1 );
3170             aMesh->RemoveElement( tr2 );
3171             // remove middle node (9)
3172             if ( N1[4]->NbInverseElements() == 0 )
3173               aMesh->RemoveNode( N1[4] );
3174             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3175               aMesh->RemoveNode( N1[6] );
3176             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3177               aMesh->RemoveNode( N2[6] );
3178           }
3179         }
3180         else if ( Ok13 )
3181         {
3182           mapEl_setLi.erase( tr3 );
3183           mapLi_listEl.erase( *link13 );
3184           if ( tr1->NbNodes() == 3 ) {
3185             const SMDS_MeshElement* newElem = 0;
3186             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3187             myLastCreatedElems.Append(newElem);
3188             AddToSameGroups( newElem, tr1, aMesh );
3189             int aShapeId = tr1->getshapeId();
3190             if ( aShapeId )
3191               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3192             aMesh->RemoveElement( tr1 );
3193             aMesh->RemoveElement( tr3 );
3194           }
3195           else {
3196             vector< const SMDS_MeshNode* > N1;
3197             vector< const SMDS_MeshNode* > N2;
3198             getNodesFromTwoTria(tr1,tr3,N1,N2);
3199             // now we receive following N1 and N2 (using numeration as above image)
3200             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3201             // i.e. first nodes from both arrays form a new diagonal
3202             const SMDS_MeshNode* aNodes[8];
3203             aNodes[0] = N1[0];
3204             aNodes[1] = N1[1];
3205             aNodes[2] = N2[0];
3206             aNodes[3] = N2[1];
3207             aNodes[4] = N1[3];
3208             aNodes[5] = N2[5];
3209             aNodes[6] = N2[3];
3210             aNodes[7] = N1[5];
3211             const SMDS_MeshElement* newElem = 0;
3212             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3213               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3214                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3215             else
3216               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3217                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3218             myLastCreatedElems.Append(newElem);
3219             AddToSameGroups( newElem, tr1, aMesh );
3220             int aShapeId = tr1->getshapeId();
3221             if ( aShapeId )
3222               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3223             aMesh->RemoveElement( tr1 );
3224             aMesh->RemoveElement( tr3 );
3225             // remove middle node (9)
3226             if ( N1[4]->NbInverseElements() == 0 )
3227               aMesh->RemoveNode( N1[4] );
3228             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3229               aMesh->RemoveNode( N1[6] );
3230             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3231               aMesh->RemoveNode( N2[6] );
3232           }
3233         }
3234
3235         // Next element to fuse: the rejected one
3236         if ( tr3 )
3237           startElem = Ok12 ? tr3 : tr2;
3238
3239       } // if ( startElem )
3240     } // while ( startElem || !startLinks.empty() )
3241   } // while ( ! mapEl_setLi.empty() )
3242
3243   return true;
3244 }
3245
3246
3247 /*#define DUMPSO(txt) \
3248 //  cout << txt << endl;
3249 //=============================================================================
3250 //
3251 //
3252 //
3253 //=============================================================================
3254 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3255 {
3256 if ( i1 == i2 )
3257 return;
3258 int tmp = idNodes[ i1 ];
3259 idNodes[ i1 ] = idNodes[ i2 ];
3260 idNodes[ i2 ] = tmp;
3261 gp_Pnt Ptmp = P[ i1 ];
3262 P[ i1 ] = P[ i2 ];
3263 P[ i2 ] = Ptmp;
3264 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3265 }
3266
3267 //=======================================================================
3268 //function : SortQuadNodes
3269 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3270 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3271 //           1 or 2 else 0.
3272 //=======================================================================
3273
3274 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3275 int               idNodes[] )
3276 {
3277   gp_Pnt P[4];
3278   int i;
3279   for ( i = 0; i < 4; i++ ) {
3280     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3281     if ( !n ) return 0;
3282     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3283   }
3284
3285   gp_Vec V1(P[0], P[1]);
3286   gp_Vec V2(P[0], P[2]);
3287   gp_Vec V3(P[0], P[3]);
3288
3289   gp_Vec Cross1 = V1 ^ V2;
3290   gp_Vec Cross2 = V2 ^ V3;
3291
3292   i = 0;
3293   if (Cross1.Dot(Cross2) < 0)
3294   {
3295     Cross1 = V2 ^ V1;
3296     Cross2 = V1 ^ V3;
3297
3298     if (Cross1.Dot(Cross2) < 0)
3299       i = 2;
3300     else
3301       i = 1;
3302     swap ( i, i + 1, idNodes, P );
3303
3304     //     for ( int ii = 0; ii < 4; ii++ ) {
3305     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3306     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3307     //     }
3308   }
3309   return i;
3310 }
3311
3312 //=======================================================================
3313 //function : SortHexaNodes
3314 //purpose  : Set 8 nodes of a hexahedron in a good order.
3315 //           Return success status
3316 //=======================================================================
3317
3318 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3319                                       int               idNodes[] )
3320 {
3321   gp_Pnt P[8];
3322   int i;
3323   DUMPSO( "INPUT: ========================================");
3324   for ( i = 0; i < 8; i++ ) {
3325     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3326     if ( !n ) return false;
3327     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3328     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3329   }
3330   DUMPSO( "========================================");
3331
3332
3333   set<int> faceNodes;  // ids of bottom face nodes, to be found
3334   set<int> checkedId1; // ids of tried 2-nd nodes
3335   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3336   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3337   int iMin, iLoop1 = 0;
3338
3339   // Loop to try the 2-nd nodes
3340
3341   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3342   {
3343     // Find not checked 2-nd node
3344     for ( i = 1; i < 8; i++ )
3345       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3346         int id1 = idNodes[i];
3347         swap ( 1, i, idNodes, P );
3348         checkedId1.insert ( id1 );
3349         break;
3350       }
3351
3352     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3353     // ie that all but meybe one (id3 which is on the same face) nodes
3354     // lay on the same side from the triangle plane.
3355
3356     bool manyInPlane = false; // more than 4 nodes lay in plane
3357     int iLoop2 = 0;
3358     while ( ++iLoop2 < 6 ) {
3359
3360       // get 1-2-3 plane coeffs
3361       Standard_Real A, B, C, D;
3362       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3363       if ( N.SquareMagnitude() > gp::Resolution() )
3364       {
3365         gp_Pln pln ( P[0], N );
3366         pln.Coefficients( A, B, C, D );
3367
3368         // find the node (iMin) closest to pln
3369         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3370         set<int> idInPln;
3371         for ( i = 3; i < 8; i++ ) {
3372           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3373           if ( fabs( dist[i] ) < minDist ) {
3374             minDist = fabs( dist[i] );
3375             iMin = i;
3376           }
3377           if ( fabs( dist[i] ) <= tol )
3378             idInPln.insert( idNodes[i] );
3379         }
3380
3381         // there should not be more than 4 nodes in bottom plane
3382         if ( idInPln.size() > 1 )
3383         {
3384           DUMPSO( "### idInPln.size() = " << idInPln.size());
3385           // idInPlane does not contain the first 3 nodes
3386           if ( manyInPlane || idInPln.size() == 5)
3387             return false; // all nodes in one plane
3388           manyInPlane = true;
3389
3390           // set the 1-st node to be not in plane
3391           for ( i = 3; i < 8; i++ ) {
3392             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3393               DUMPSO( "### Reset 0-th node");
3394               swap( 0, i, idNodes, P );
3395               break;
3396             }
3397           }
3398
3399           // reset to re-check second nodes
3400           leastDist = DBL_MAX;
3401           faceNodes.clear();
3402           checkedId1.clear();
3403           iLoop1 = 0;
3404           break; // from iLoop2;
3405         }
3406
3407         // check that the other 4 nodes are on the same side
3408         bool sameSide = true;
3409         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3410         for ( i = 3; sameSide && i < 8; i++ ) {
3411           if ( i != iMin )
3412             sameSide = ( isNeg == dist[i] <= 0.);
3413         }
3414
3415         // keep best solution
3416         if ( sameSide && minDist < leastDist ) {
3417           leastDist = minDist;
3418           faceNodes.clear();
3419           faceNodes.insert( idNodes[ 1 ] );
3420           faceNodes.insert( idNodes[ 2 ] );
3421           faceNodes.insert( idNodes[ iMin ] );
3422           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3423                   << " leastDist = " << leastDist);
3424           if ( leastDist <= DBL_MIN )
3425             break;
3426         }
3427       }
3428
3429       // set next 3-d node to check
3430       int iNext = 2 + iLoop2;
3431       if ( iNext < 8 ) {
3432         DUMPSO( "Try 2-nd");
3433         swap ( 2, iNext, idNodes, P );
3434       }
3435     } // while ( iLoop2 < 6 )
3436   } // iLoop1
3437
3438   if ( faceNodes.empty() ) return false;
3439
3440   // Put the faceNodes in proper places
3441   for ( i = 4; i < 8; i++ ) {
3442     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3443       // find a place to put
3444       int iTo = 1;
3445       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3446         iTo++;
3447       DUMPSO( "Set faceNodes");
3448       swap ( iTo, i, idNodes, P );
3449     }
3450   }
3451
3452
3453   // Set nodes of the found bottom face in good order
3454   DUMPSO( " Found bottom face: ");
3455   i = SortQuadNodes( theMesh, idNodes );
3456   if ( i ) {
3457     gp_Pnt Ptmp = P[ i ];
3458     P[ i ] = P[ i+1 ];
3459     P[ i+1 ] = Ptmp;
3460   }
3461   //   else
3462   //     for ( int ii = 0; ii < 4; ii++ ) {
3463   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3464   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3465   //    }
3466
3467   // Gravity center of the top and bottom faces
3468   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3469   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3470
3471   // Get direction from the bottom to the top face
3472   gp_Vec upDir ( aGCb, aGCt );
3473   Standard_Real upDirSize = upDir.Magnitude();
3474   if ( upDirSize <= gp::Resolution() ) return false;
3475   upDir / upDirSize;
3476
3477   // Assure that the bottom face normal points up
3478   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3479   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3480   if ( Nb.Dot( upDir ) < 0 ) {
3481     DUMPSO( "Reverse bottom face");
3482     swap( 1, 3, idNodes, P );
3483   }
3484
3485   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3486   Standard_Real minDist = DBL_MAX;
3487   for ( i = 4; i < 8; i++ ) {
3488     // projection of P[i] to the plane defined by P[0] and upDir
3489     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3490     Standard_Real sqDist = P[0].SquareDistance( Pp );
3491     if ( sqDist < minDist ) {
3492       minDist = sqDist;
3493       iMin = i;
3494     }
3495   }
3496   DUMPSO( "Set 4-th");
3497   swap ( 4, iMin, idNodes, P );
3498
3499   // Set nodes of the top face in good order
3500   DUMPSO( "Sort top face");
3501   i = SortQuadNodes( theMesh, &idNodes[4] );
3502   if ( i ) {
3503     i += 4;
3504     gp_Pnt Ptmp = P[ i ];
3505     P[ i ] = P[ i+1 ];
3506     P[ i+1 ] = Ptmp;
3507   }
3508
3509   // Assure that direction of the top face normal is from the bottom face
3510   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3511   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3512   if ( Nt.Dot( upDir ) < 0 ) {
3513     DUMPSO( "Reverse top face");
3514     swap( 5, 7, idNodes, P );
3515   }
3516
3517   //   DUMPSO( "OUTPUT: ========================================");
3518   //   for ( i = 0; i < 8; i++ ) {
3519   //     float *p = ugrid->GetPoint(idNodes[i]);
3520   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3521   //   }
3522
3523   return true;
3524 }*/
3525
3526 //================================================================================
3527 /*!
3528  * \brief Return nodes linked to the given one
3529  * \param theNode - the node
3530  * \param linkedNodes - the found nodes
3531  * \param type - the type of elements to check
3532  *
3533  * Medium nodes are ignored
3534  */
3535 //================================================================================
3536
3537 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3538                                        TIDSortedElemSet &   linkedNodes,
3539                                        SMDSAbs_ElementType  type )
3540 {
3541   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3542   while ( elemIt->more() )
3543   {
3544     const SMDS_MeshElement* elem = elemIt->next();
3545     if(elem->GetType() == SMDSAbs_0DElement)
3546       continue;
3547
3548     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3549     if ( elem->GetType() == SMDSAbs_Volume )
3550     {
3551       SMDS_VolumeTool vol( elem );
3552       while ( nodeIt->more() ) {
3553         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3554         if ( theNode != n && vol.IsLinked( theNode, n ))
3555           linkedNodes.insert( n );
3556       }
3557     }
3558     else
3559     {
3560       for ( int i = 0; nodeIt->more(); ++i ) {
3561         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3562         if ( n == theNode ) {
3563           int iBefore = i - 1;
3564           int iAfter  = i + 1;
3565           if ( elem->IsQuadratic() ) {
3566             int nb = elem->NbNodes() / 2;
3567             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3568             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3569           }
3570           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3571           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3572         }
3573       }
3574     }
3575   }
3576 }
3577
3578 //=======================================================================
3579 //function : laplacianSmooth
3580 //purpose  : pulls theNode toward the center of surrounding nodes directly
3581 //           connected to that node along an element edge
3582 //=======================================================================
3583
3584 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3585                      const Handle(Geom_Surface)&          theSurface,
3586                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3587 {
3588   // find surrounding nodes
3589
3590   TIDSortedElemSet nodeSet;
3591   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3592
3593   // compute new coodrs
3594
3595   double coord[] = { 0., 0., 0. };
3596   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3597   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3598     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3599     if ( theSurface.IsNull() ) { // smooth in 3D
3600       coord[0] += node->X();
3601       coord[1] += node->Y();
3602       coord[2] += node->Z();
3603     }
3604     else { // smooth in 2D
3605       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3606       gp_XY* uv = theUVMap[ node ];
3607       coord[0] += uv->X();
3608       coord[1] += uv->Y();
3609     }
3610   }
3611   int nbNodes = nodeSet.size();
3612   if ( !nbNodes )
3613     return;
3614   coord[0] /= nbNodes;
3615   coord[1] /= nbNodes;
3616
3617   if ( !theSurface.IsNull() ) {
3618     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3619     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3620     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3621     coord[0] = p3d.X();
3622     coord[1] = p3d.Y();
3623     coord[2] = p3d.Z();
3624   }
3625   else
3626     coord[2] /= nbNodes;
3627
3628   // move node
3629
3630   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3631 }
3632
3633 //=======================================================================
3634 //function : centroidalSmooth
3635 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3636 //           surrounding elements
3637 //=======================================================================
3638
3639 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3640                       const Handle(Geom_Surface)&          theSurface,
3641                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3642 {
3643   gp_XYZ aNewXYZ(0.,0.,0.);
3644   SMESH::Controls::Area anAreaFunc;
3645   double totalArea = 0.;
3646   int nbElems = 0;
3647
3648   // compute new XYZ
3649
3650   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3651   while ( elemIt->more() )
3652   {
3653     const SMDS_MeshElement* elem = elemIt->next();
3654     nbElems++;
3655
3656     gp_XYZ elemCenter(0.,0.,0.);
3657     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3658     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3659     int nn = elem->NbNodes();
3660     if(elem->IsQuadratic()) nn = nn/2;
3661     int i=0;
3662     //while ( itN->more() ) {
3663     while ( i<nn ) {
3664       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3665       i++;
3666       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3667       aNodePoints.push_back( aP );
3668       if ( !theSurface.IsNull() ) { // smooth in 2D
3669         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3670         gp_XY* uv = theUVMap[ aNode ];
3671         aP.SetCoord( uv->X(), uv->Y(), 0. );
3672       }
3673       elemCenter += aP;
3674     }
3675     double elemArea = anAreaFunc.GetValue( aNodePoints );
3676     totalArea += elemArea;
3677     elemCenter /= nn;
3678     aNewXYZ += elemCenter * elemArea;
3679   }
3680   aNewXYZ /= totalArea;
3681   if ( !theSurface.IsNull() ) {
3682     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3683     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3684   }
3685
3686   // move node
3687
3688   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3689 }
3690
3691 //=======================================================================
3692 //function : getClosestUV
3693 //purpose  : return UV of closest projection
3694 //=======================================================================
3695
3696 static bool getClosestUV (Extrema_GenExtPS& projector,
3697                           const gp_Pnt&     point,
3698                           gp_XY &           result)
3699 {
3700   projector.Perform( point );
3701   if ( projector.IsDone() ) {
3702     double u, v, minVal = DBL_MAX;
3703     for ( int i = projector.NbExt(); i > 0; i-- )
3704       if ( projector.SquareDistance( i ) < minVal ) {
3705         minVal = projector.SquareDistance( i );
3706         projector.Point( i ).Parameter( u, v );
3707       }
3708     result.SetCoord( u, v );
3709     return true;
3710   }
3711   return false;
3712 }
3713
3714 //=======================================================================
3715 //function : Smooth
3716 //purpose  : Smooth theElements during theNbIterations or until a worst
3717 //           element has aspect ratio <= theTgtAspectRatio.
3718 //           Aspect Ratio varies in range [1.0, inf].
3719 //           If theElements is empty, the whole mesh is smoothed.
3720 //           theFixedNodes contains additionally fixed nodes. Nodes built
3721 //           on edges and boundary nodes are always fixed.
3722 //=======================================================================
3723
3724 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3725                                set<const SMDS_MeshNode*> & theFixedNodes,
3726                                const SmoothMethod          theSmoothMethod,
3727                                const int                   theNbIterations,
3728                                double                      theTgtAspectRatio,
3729                                const bool                  the2D)
3730 {
3731   myLastCreatedElems.Clear();
3732   myLastCreatedNodes.Clear();
3733
3734   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3735
3736   if ( theTgtAspectRatio < 1.0 )
3737     theTgtAspectRatio = 1.0;
3738
3739   const double disttol = 1.e-16;
3740
3741   SMESH::Controls::AspectRatio aQualityFunc;
3742
3743   SMESHDS_Mesh* aMesh = GetMeshDS();
3744
3745   if ( theElems.empty() ) {
3746     // add all faces to theElems
3747     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3748     while ( fIt->more() ) {
3749       const SMDS_MeshElement* face = fIt->next();
3750       theElems.insert( theElems.end(), face );
3751     }
3752   }
3753   // get all face ids theElems are on
3754   set< int > faceIdSet;
3755   TIDSortedElemSet::iterator itElem;
3756   if ( the2D )
3757     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3758       int fId = FindShape( *itElem );
3759       // check that corresponding submesh exists and a shape is face
3760       if (fId &&
3761           faceIdSet.find( fId ) == faceIdSet.end() &&
3762           aMesh->MeshElements( fId )) {
3763         TopoDS_Shape F = aMesh->IndexToShape( fId );
3764         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3765           faceIdSet.insert( fId );
3766       }
3767     }
3768   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3769
3770   // ===============================================
3771   // smooth elements on each TopoDS_Face separately
3772   // ===============================================
3773
3774   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3775   for ( ; fId != faceIdSet.rend(); ++fId ) {
3776     // get face surface and submesh
3777     Handle(Geom_Surface) surface;
3778     SMESHDS_SubMesh* faceSubMesh = 0;
3779     TopoDS_Face face;
3780     double fToler2 = 0, f,l;
3781     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3782     bool isUPeriodic = false, isVPeriodic = false;
3783     if ( *fId ) {
3784       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3785       surface = BRep_Tool::Surface( face );
3786       faceSubMesh = aMesh->MeshElements( *fId );
3787       fToler2 = BRep_Tool::Tolerance( face );
3788       fToler2 *= fToler2 * 10.;
3789       isUPeriodic = surface->IsUPeriodic();
3790       if ( isUPeriodic )
3791         surface->UPeriod();
3792       isVPeriodic = surface->IsVPeriodic();
3793       if ( isVPeriodic )
3794         surface->VPeriod();
3795       surface->Bounds( u1, u2, v1, v2 );
3796     }
3797     // ---------------------------------------------------------
3798     // for elements on a face, find movable and fixed nodes and
3799     // compute UV for them
3800     // ---------------------------------------------------------
3801     bool checkBoundaryNodes = false;
3802     bool isQuadratic = false;
3803     set<const SMDS_MeshNode*> setMovableNodes;
3804     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3805     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3806     list< const SMDS_MeshElement* > elemsOnFace;
3807
3808     Extrema_GenExtPS projector;
3809     GeomAdaptor_Surface surfAdaptor;
3810     if ( !surface.IsNull() ) {
3811       surfAdaptor.Load( surface );
3812       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3813     }
3814     int nbElemOnFace = 0;
3815     itElem = theElems.begin();
3816     // loop on not yet smoothed elements: look for elems on a face
3817     while ( itElem != theElems.end() ) {
3818       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3819         break; // all elements found
3820
3821       const SMDS_MeshElement* elem = *itElem;
3822       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3823            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3824         ++itElem;
3825         continue;
3826       }
3827       elemsOnFace.push_back( elem );
3828       theElems.erase( itElem++ );
3829       nbElemOnFace++;
3830
3831       if ( !isQuadratic )
3832         isQuadratic = elem->IsQuadratic();
3833
3834       // get movable nodes of elem
3835       const SMDS_MeshNode* node;
3836       SMDS_TypeOfPosition posType;
3837       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3838       int nn = 0, nbn =  elem->NbNodes();
3839       if(elem->IsQuadratic())
3840         nbn = nbn/2;
3841       while ( nn++ < nbn ) {
3842         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3843         const SMDS_PositionPtr& pos = node->GetPosition();
3844         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3845         if (posType != SMDS_TOP_EDGE &&
3846             posType != SMDS_TOP_VERTEX &&
3847             theFixedNodes.find( node ) == theFixedNodes.end())
3848         {
3849           // check if all faces around the node are on faceSubMesh
3850           // because a node on edge may be bound to face
3851           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3852           bool all = true;
3853           if ( faceSubMesh ) {
3854             while ( eIt->more() && all ) {
3855               const SMDS_MeshElement* e = eIt->next();
3856               all = faceSubMesh->Contains( e );
3857             }
3858           }
3859           if ( all )
3860             setMovableNodes.insert( node );
3861           else
3862             checkBoundaryNodes = true;
3863         }
3864         if ( posType == SMDS_TOP_3DSPACE )
3865           checkBoundaryNodes = true;
3866       }
3867
3868       if ( surface.IsNull() )
3869         continue;
3870
3871       // get nodes to check UV
3872       list< const SMDS_MeshNode* > uvCheckNodes;
3873       itN = elem->nodesIterator();
3874       nn = 0; nbn =  elem->NbNodes();
3875       if(elem->IsQuadratic())
3876         nbn = nbn/2;
3877       while ( nn++ < nbn ) {
3878         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3879         if ( uvMap.find( node ) == uvMap.end() )
3880           uvCheckNodes.push_back( node );
3881         // add nodes of elems sharing node
3882         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3883         //         while ( eIt->more() ) {
3884         //           const SMDS_MeshElement* e = eIt->next();
3885         //           if ( e != elem ) {
3886         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3887         //             while ( nIt->more() ) {
3888         //               const SMDS_MeshNode* n =
3889         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3890         //               if ( uvMap.find( n ) == uvMap.end() )
3891         //                 uvCheckNodes.push_back( n );
3892         //             }
3893         //           }
3894         //         }
3895       }
3896       // check UV on face
3897       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3898       for ( ; n != uvCheckNodes.end(); ++n ) {
3899         node = *n;
3900         gp_XY uv( 0, 0 );
3901         const SMDS_PositionPtr& pos = node->GetPosition();
3902         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3903         // get existing UV
3904         switch ( posType ) {
3905         case SMDS_TOP_FACE: {
3906           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3907           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3908           break;
3909         }
3910         case SMDS_TOP_EDGE: {
3911           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3912           Handle(Geom2d_Curve) pcurve;
3913           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3914             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3915           if ( !pcurve.IsNull() ) {
3916             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3917             uv = pcurve->Value( u ).XY();
3918           }
3919           break;
3920         }
3921         case SMDS_TOP_VERTEX: {
3922           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3923           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3924             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3925           break;
3926         }
3927         default:;
3928         }
3929         // check existing UV
3930         bool project = true;
3931         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3932         double dist1 = DBL_MAX, dist2 = 0;
3933         if ( posType != SMDS_TOP_3DSPACE ) {
3934           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3935           project = dist1 > fToler2;
3936         }
3937         if ( project ) { // compute new UV
3938           gp_XY newUV;
3939           if ( !getClosestUV( projector, pNode, newUV )) {
3940             MESSAGE("Node Projection Failed " << node);
3941           }
3942           else {
3943             if ( isUPeriodic )
3944               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3945             if ( isVPeriodic )
3946               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3947             // check new UV
3948             if ( posType != SMDS_TOP_3DSPACE )
3949               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3950             if ( dist2 < dist1 )
3951               uv = newUV;
3952           }
3953         }
3954         // store UV in the map
3955         listUV.push_back( uv );
3956         uvMap.insert( make_pair( node, &listUV.back() ));
3957       }
3958     } // loop on not yet smoothed elements
3959
3960     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3961       checkBoundaryNodes = true;
3962
3963     // fix nodes on mesh boundary
3964
3965     if ( checkBoundaryNodes ) {
3966       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3967       map< SMESH_TLink, int >::iterator link_nb;
3968       // put all elements links to linkNbMap
3969       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3970       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3971         const SMDS_MeshElement* elem = (*elemIt);
3972         int nbn =  elem->NbCornerNodes();
3973         // loop on elem links: insert them in linkNbMap
3974         for ( int iN = 0; iN < nbn; ++iN ) {
3975           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3976           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3977           SMESH_TLink link( n1, n2 );
3978           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3979           link_nb->second++;
3980         }
3981       }
3982       // remove nodes that are in links encountered only once from setMovableNodes
3983       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3984         if ( link_nb->second == 1 ) {
3985           setMovableNodes.erase( link_nb->first.node1() );
3986           setMovableNodes.erase( link_nb->first.node2() );
3987         }
3988       }
3989     }
3990
3991     // -----------------------------------------------------
3992     // for nodes on seam edge, compute one more UV ( uvMap2 );
3993     // find movable nodes linked to nodes on seam and which
3994     // are to be smoothed using the second UV ( uvMap2 )
3995     // -----------------------------------------------------
3996
3997     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3998     if ( !surface.IsNull() ) {
3999       TopExp_Explorer eExp( face, TopAbs_EDGE );
4000       for ( ; eExp.More(); eExp.Next() ) {
4001         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4002         if ( !BRep_Tool::IsClosed( edge, face ))
4003           continue;
4004         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4005         if ( !sm ) continue;
4006         // find out which parameter varies for a node on seam
4007         double f,l;
4008         gp_Pnt2d uv1, uv2;
4009         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4010         if ( pcurve.IsNull() ) continue;
4011         uv1 = pcurve->Value( f );
4012         edge.Reverse();
4013         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4014         if ( pcurve.IsNull() ) continue;
4015         uv2 = pcurve->Value( f );
4016         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4017         // assure uv1 < uv2
4018         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
4019           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
4020         }
4021         // get nodes on seam and its vertices
4022         list< const SMDS_MeshNode* > seamNodes;
4023         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4024         while ( nSeamIt->more() ) {
4025           const SMDS_MeshNode* node = nSeamIt->next();
4026           if ( !isQuadratic || !IsMedium( node ))
4027             seamNodes.push_back( node );
4028         }
4029         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4030         for ( ; vExp.More(); vExp.Next() ) {
4031           sm = aMesh->MeshElements( vExp.Current() );
4032           if ( sm ) {
4033             nSeamIt = sm->GetNodes();
4034             while ( nSeamIt->more() )
4035               seamNodes.push_back( nSeamIt->next() );
4036           }
4037         }
4038         // loop on nodes on seam
4039         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4040         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4041           const SMDS_MeshNode* nSeam = *noSeIt;
4042           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4043           if ( n_uv == uvMap.end() )
4044             continue;
4045           // set the first UV
4046           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4047           // set the second UV
4048           listUV.push_back( *n_uv->second );
4049           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4050           if ( uvMap2.empty() )
4051             uvMap2 = uvMap; // copy the uvMap contents
4052           uvMap2[ nSeam ] = &listUV.back();
4053
4054           // collect movable nodes linked to ones on seam in nodesNearSeam
4055           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4056           while ( eIt->more() ) {
4057             const SMDS_MeshElement* e = eIt->next();
4058             int nbUseMap1 = 0, nbUseMap2 = 0;
4059             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4060             int nn = 0, nbn =  e->NbNodes();
4061             if(e->IsQuadratic()) nbn = nbn/2;
4062             while ( nn++ < nbn )
4063             {
4064               const SMDS_MeshNode* n =
4065                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4066               if (n == nSeam ||
4067                   setMovableNodes.find( n ) == setMovableNodes.end() )
4068                 continue;
4069               // add only nodes being closer to uv2 than to uv1
4070               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4071                            0.5 * ( n->Y() + nSeam->Y() ),
4072                            0.5 * ( n->Z() + nSeam->Z() ));
4073               gp_XY uv;
4074               getClosestUV( projector, pMid, uv );
4075               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
4076                 nodesNearSeam.insert( n );
4077                 nbUseMap2++;
4078               }
4079               else
4080                 nbUseMap1++;
4081             }
4082             // for centroidalSmooth all element nodes must
4083             // be on one side of a seam
4084             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4085               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4086               nn = 0;
4087               while ( nn++ < nbn ) {
4088                 const SMDS_MeshNode* n =
4089                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4090                 setMovableNodes.erase( n );
4091               }
4092             }
4093           }
4094         } // loop on nodes on seam
4095       } // loop on edge of a face
4096     } // if ( !face.IsNull() )
4097
4098     if ( setMovableNodes.empty() ) {
4099       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4100       continue; // goto next face
4101     }
4102
4103     // -------------
4104     // SMOOTHING //
4105     // -------------
4106
4107     int it = -1;
4108     double maxRatio = -1., maxDisplacement = -1.;
4109     set<const SMDS_MeshNode*>::iterator nodeToMove;
4110     for ( it = 0; it < theNbIterations; it++ ) {
4111       maxDisplacement = 0.;
4112       nodeToMove = setMovableNodes.begin();
4113       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4114         const SMDS_MeshNode* node = (*nodeToMove);
4115         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4116
4117         // smooth
4118         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4119         if ( theSmoothMethod == LAPLACIAN )
4120           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4121         else
4122           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4123
4124         // node displacement
4125         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4126         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4127         if ( aDispl > maxDisplacement )
4128           maxDisplacement = aDispl;
4129       }
4130       // no node movement => exit
4131       //if ( maxDisplacement < 1.e-16 ) {
4132       if ( maxDisplacement < disttol ) {
4133         MESSAGE("-- no node movement --");
4134         break;
4135       }
4136
4137       // check elements quality
4138       maxRatio  = 0;
4139       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4140       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4141         const SMDS_MeshElement* elem = (*elemIt);
4142         if ( !elem || elem->GetType() != SMDSAbs_Face )
4143           continue;
4144         SMESH::Controls::TSequenceOfXYZ aPoints;
4145         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4146           double aValue = aQualityFunc.GetValue( aPoints );
4147           if ( aValue > maxRatio )
4148             maxRatio = aValue;
4149         }
4150       }
4151       if ( maxRatio <= theTgtAspectRatio ) {
4152         MESSAGE("-- quality achived --");
4153         break;
4154       }
4155       if (it+1 == theNbIterations) {
4156         MESSAGE("-- Iteration limit exceeded --");
4157       }
4158     } // smoothing iterations
4159
4160     MESSAGE(" Face id: " << *fId <<
4161             " Nb iterstions: " << it <<
4162             " Displacement: " << maxDisplacement <<
4163             " Aspect Ratio " << maxRatio);
4164
4165     // ---------------------------------------
4166     // new nodes positions are computed,
4167     // record movement in DS and set new UV
4168     // ---------------------------------------
4169     nodeToMove = setMovableNodes.begin();
4170     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4171       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4172       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4173       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4174       if ( node_uv != uvMap.end() ) {
4175         gp_XY* uv = node_uv->second;
4176         node->SetPosition
4177           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4178       }
4179     }
4180
4181     // move medium nodes of quadratic elements
4182     if ( isQuadratic )
4183     {
4184       SMESH_MesherHelper helper( *GetMesh() );
4185       helper.SetSubShape( face );
4186       vector<const SMDS_MeshNode*> nodes;
4187       bool checkUV;
4188       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4189       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4190       {
4191         const SMDS_MeshElement* QF = *elemIt;
4192         if ( QF->IsQuadratic() )
4193         {
4194           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4195                         SMDS_MeshElement::iterator() );
4196           nodes.push_back( nodes[0] );
4197           gp_Pnt xyz;
4198           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4199           {
4200             if ( !surface.IsNull() )
4201             {
4202               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4203               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4204               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4205               xyz = surface->Value( uv.X(), uv.Y() );
4206             }
4207             else {
4208               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4209             }
4210             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4211               // we have to move a medium node
4212               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4213           }
4214         }
4215       }
4216     }
4217
4218   } // loop on face ids
4219
4220 }
4221
4222 //=======================================================================
4223 //function : isReverse
4224 //purpose  : Return true if normal of prevNodes is not co-directied with
4225 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4226 //           iNotSame is where prevNodes and nextNodes are different.
4227 //           If result is true then future volume orientation is OK
4228 //=======================================================================
4229
4230 static bool isReverse(const SMDS_MeshElement*             face,
4231                       const vector<const SMDS_MeshNode*>& prevNodes,
4232                       const vector<const SMDS_MeshNode*>& nextNodes,
4233                       const int                           iNotSame)
4234 {
4235
4236   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4237   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4238   gp_XYZ extrDir( pN - pP ), faceNorm;
4239   SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4240
4241   return faceNorm * extrDir < 0.0;
4242 }
4243
4244 //=======================================================================
4245 /*!
4246  * \brief Create elements by sweeping an element
4247  * \param elem - element to sweep
4248  * \param newNodesItVec - nodes generated from each node of the element
4249  * \param newElems - generated elements
4250  * \param nbSteps - number of sweeping steps
4251  * \param srcElements - to append elem for each generated element
4252  */
4253 //=======================================================================
4254
4255 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4256                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4257                                     list<const SMDS_MeshElement*>&        newElems,
4258                                     const int                             nbSteps,
4259                                     SMESH_SequenceOfElemPtr&              srcElements)
4260 {
4261   //MESSAGE("sweepElement " << nbSteps);
4262   SMESHDS_Mesh* aMesh = GetMeshDS();
4263
4264   const int           nbNodes = elem->NbNodes();
4265   const int         nbCorners = elem->NbCornerNodes();
4266   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4267                                                           polyhedron creation !!! */
4268   // Loop on elem nodes:
4269   // find new nodes and detect same nodes indices
4270   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4271   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4272   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4273   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4274
4275   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4276   vector<int> sames(nbNodes);
4277   vector<bool> isSingleNode(nbNodes);
4278
4279   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4280     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4281     const SMDS_MeshNode*                         node = nnIt->first;
4282     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4283     if ( listNewNodes.empty() )
4284       return;
4285
4286     itNN   [ iNode ] = listNewNodes.begin();
4287     prevNod[ iNode ] = node;
4288     nextNod[ iNode ] = listNewNodes.front();
4289
4290     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4291                                                              corner node of linear */
4292     if ( prevNod[ iNode ] != nextNod [ iNode ])
4293       nbDouble += !isSingleNode[iNode];
4294
4295     if( iNode < nbCorners ) { // check corners only
4296       if ( prevNod[ iNode ] == nextNod [ iNode ])
4297         sames[nbSame++] = iNode;
4298       else
4299         iNotSameNode = iNode;
4300     }
4301   }
4302
4303   if ( nbSame == nbNodes || nbSame > 2) {
4304     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4305     return;
4306   }
4307
4308   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4309   {
4310     // fix nodes order to have bottom normal external
4311     if ( baseType == SMDSEntity_Polygon )
4312     {
4313       std::reverse( itNN.begin(), itNN.end() );
4314       std::reverse( prevNod.begin(), prevNod.end() );
4315       std::reverse( midlNod.begin(), midlNod.end() );
4316       std::reverse( nextNod.begin(), nextNod.end() );
4317       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4318     }
4319     else
4320     {
4321       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4322       SMDS_MeshCell::applyInterlace( ind, itNN );
4323       SMDS_MeshCell::applyInterlace( ind, prevNod );
4324       SMDS_MeshCell::applyInterlace( ind, nextNod );
4325       SMDS_MeshCell::applyInterlace( ind, midlNod );
4326       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4327       if ( nbSame > 0 )
4328       {
4329         sames[nbSame] = iNotSameNode;
4330         for ( int j = 0; j <= nbSame; ++j )
4331           for ( size_t i = 0; i < ind.size(); ++i )
4332             if ( ind[i] == sames[j] )
4333             {
4334               sames[j] = i;
4335               break;
4336             }
4337         iNotSameNode = sames[nbSame];
4338       }
4339     }
4340   }
4341
4342   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4343   if ( nbSame > 0 ) {
4344     iSameNode    = sames[ nbSame-1 ];
4345     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4346     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4347     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4348   }
4349
4350   // make new elements
4351   for (int iStep = 0; iStep < nbSteps; iStep++ )
4352   {
4353     // get next nodes
4354     for ( iNode = 0; iNode < nbNodes; iNode++ )
4355     {
4356       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4357       nextNod[ iNode ] = *itNN[ iNode ]++;
4358     }
4359
4360     SMDS_MeshElement* aNewElem = 0;
4361     /*if(!elem->IsPoly())*/ {
4362       switch ( baseType ) {
4363       case SMDSEntity_0D:
4364       case SMDSEntity_Node: { // sweep NODE
4365         if ( nbSame == 0 ) {
4366           if ( isSingleNode[0] )
4367             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4368           else
4369             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4370         }
4371         else
4372           return;
4373         break;
4374       }
4375       case SMDSEntity_Edge: { // sweep EDGE
4376         if ( nbDouble == 0 )
4377         {
4378           if ( nbSame == 0 ) // ---> quadrangle
4379             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4380                                       nextNod[ 1 ], nextNod[ 0 ] );
4381           else               // ---> triangle
4382             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4383                                       nextNod[ iNotSameNode ] );
4384         }
4385         else                 // ---> polygon
4386         {
4387           vector<const SMDS_MeshNode*> poly_nodes;
4388           poly_nodes.push_back( prevNod[0] );
4389           poly_nodes.push_back( prevNod[1] );
4390           if ( prevNod[1] != nextNod[1] )
4391           {
4392             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4393             poly_nodes.push_back( nextNod[1] );
4394           }
4395           if ( prevNod[0] != nextNod[0] )
4396           {
4397             poly_nodes.push_back( nextNod[0] );
4398             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4399           }
4400           switch ( poly_nodes.size() ) {
4401           case 3:
4402             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4403             break;
4404           case 4:
4405             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4406                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4407             break;
4408           default:
4409             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4410           }
4411         }
4412         break;
4413       }
4414       case SMDSEntity_Triangle: // TRIANGLE --->
4415         {
4416           if ( nbDouble > 0 ) break;
4417           if ( nbSame == 0 )       // ---> pentahedron
4418             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4419                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4420
4421           else if ( nbSame == 1 )  // ---> pyramid
4422             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4423                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4424                                          nextNod[ iSameNode ]);
4425
4426           else // 2 same nodes:       ---> tetrahedron
4427             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4428                                          nextNod[ iNotSameNode ]);
4429           break;
4430         }
4431       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4432         {
4433           if ( nbSame == 2 )
4434             return;
4435           if ( nbDouble+nbSame == 2 )
4436           {
4437             if(nbSame==0) {      // ---> quadratic quadrangle
4438               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4439                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4440             }
4441             else { //(nbSame==1) // ---> quadratic triangle
4442               if(sames[0]==2) {
4443                 return; // medium node on axis
4444               }
4445               else if(sames[0]==0)
4446                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4447                                           nextNod[2], midlNod[1], prevNod[2]);
4448               else // sames[0]==1
4449                 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4450                                           midlNod[0], nextNod[2], prevNod[2]);
4451             }
4452           }
4453           else if ( nbDouble == 3 )
4454           {
4455             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4456               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4457                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4458             }
4459           }
4460           else
4461             return;
4462           break;
4463         }
4464       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4465         if ( nbDouble > 0 ) break;
4466
4467         if ( nbSame == 0 )       // ---> hexahedron
4468           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4469                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4470
4471         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4472           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4473                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4474                                        nextNod[ iSameNode ]);
4475           newElems.push_back( aNewElem );
4476           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4477                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4478                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4479         }
4480         else if ( nbSame == 2 ) { // ---> pentahedron
4481           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4482             // iBeforeSame is same too
4483             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4484                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4485                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4486           else
4487             // iAfterSame is same too
4488             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4489                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4490                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4491         }
4492         break;
4493       }
4494       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4495       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4496         if ( nbDouble+nbSame != 3 ) break;
4497         if(nbSame==0) {
4498           // --->  pentahedron with 15 nodes
4499           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4500                                        nextNod[0], nextNod[1], nextNod[2],
4501                                        prevNod[3], prevNod[4], prevNod[5],
4502                                        nextNod[3], nextNod[4], nextNod[5],
4503                                        midlNod[0], midlNod[1], midlNod[2]);
4504         }
4505         else if(nbSame==1) {
4506           // --->  2d order pyramid of 13 nodes
4507           int apex = iSameNode;
4508           int i0 = ( apex + 1 ) % nbCorners;
4509           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4510           int i0a = apex + 3;
4511           int i1a = i1 + 3;
4512           int i01 = i0 + 3;
4513           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4514                                       nextNod[i0], nextNod[i1], prevNod[apex],
4515                                       prevNod[i01], midlNod[i0],
4516                                       nextNod[i01], midlNod[i1],
4517                                       prevNod[i1a], prevNod[i0a],
4518                                       nextNod[i0a], nextNod[i1a]);
4519         }
4520         else if(nbSame==2) {
4521           // --->  2d order tetrahedron of 10 nodes
4522           int n1 = iNotSameNode;
4523           int n2 = ( n1 + 1             ) % nbCorners;
4524           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4525           int n12 = n1 + 3;
4526           int n23 = n2 + 3;
4527           int n31 = n3 + 3;
4528           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4529                                        prevNod[n12], prevNod[n23], prevNod[n31],
4530                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4531         }
4532         break;
4533       }
4534       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4535         if( nbSame == 0 ) {
4536           if ( nbDouble != 4 ) break;
4537           // --->  hexahedron with 20 nodes
4538           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4539                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4540                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4541                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4542                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4543         }
4544         else if(nbSame==1) {
4545           // ---> pyramid + pentahedron - can not be created since it is needed
4546           // additional middle node at the center of face
4547           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4548           return;
4549         }
4550         else if( nbSame == 2 ) {
4551           if ( nbDouble != 2 ) break;
4552           // --->  2d order Pentahedron with 15 nodes
4553           int n1,n2,n4,n5;
4554           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4555             // iBeforeSame is same too
4556             n1 = iBeforeSame;
4557             n2 = iOpposSame;
4558             n4 = iSameNode;
4559             n5 = iAfterSame;
4560           }
4561           else {
4562             // iAfterSame is same too
4563             n1 = iSameNode;
4564             n2 = iBeforeSame;
4565             n4 = iAfterSame;
4566             n5 = iOpposSame;
4567           }
4568           int n12 = n2 + 4;
4569           int n45 = n4 + 4;
4570           int n14 = n1 + 4;
4571           int n25 = n5 + 4;
4572           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4573                                        prevNod[n4], prevNod[n5], nextNod[n5],
4574                                        prevNod[n12], midlNod[n2], nextNod[n12],
4575                                        prevNod[n45], midlNod[n5], nextNod[n45],
4576                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4577         }
4578         break;
4579       }
4580       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4581
4582         if( nbSame == 0 && nbDouble == 9 ) {
4583           // --->  tri-quadratic hexahedron with 27 nodes
4584           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4585                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4586                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4587                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4588                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4589                                        prevNod[8], // bottom center
4590                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4591                                        nextNod[8], // top center
4592                                        midlNod[8]);// elem center
4593         }
4594         else
4595         {
4596           return;
4597         }
4598         break;
4599       }
4600       case SMDSEntity_Polygon: { // sweep POLYGON
4601
4602         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4603           // --->  hexagonal prism
4604           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4605                                        prevNod[3], prevNod[4], prevNod[5],
4606                                        nextNod[0], nextNod[1], nextNod[2],
4607                                        nextNod[3], nextNod[4], nextNod[5]);
4608         }
4609         break;
4610       }
4611       case SMDSEntity_Ball:
4612         return;
4613
4614       default:
4615         break;
4616       }
4617     }
4618
4619     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4620     {
4621       if ( baseType != SMDSEntity_Polygon )
4622       {
4623         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4624         SMDS_MeshCell::applyInterlace( ind, prevNod );
4625         SMDS_MeshCell::applyInterlace( ind, nextNod );
4626         SMDS_MeshCell::applyInterlace( ind, midlNod );
4627         SMDS_MeshCell::applyInterlace( ind, itNN );
4628         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4629         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4630       }
4631       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4632       vector<int> quantities (nbNodes + 2);
4633       polyedre_nodes.clear();
4634       quantities.clear();
4635
4636       // bottom of prism
4637       for (int inode = 0; inode < nbNodes; inode++)
4638         polyedre_nodes.push_back( prevNod[inode] );
4639       quantities.push_back( nbNodes );
4640
4641       // top of prism
4642       polyedre_nodes.push_back( nextNod[0] );
4643       for (int inode = nbNodes; inode-1; --inode )
4644         polyedre_nodes.push_back( nextNod[inode-1] );
4645       quantities.push_back( nbNodes );
4646
4647       // side faces
4648       for (int iface = 0; iface < nbNodes; iface++)
4649       {
4650         const int prevNbNodes = polyedre_nodes.size();
4651         int inextface = (iface+1) % nbNodes;
4652         polyedre_nodes.push_back( prevNod[inextface] );
4653         polyedre_nodes.push_back( prevNod[iface] );
4654         if ( prevNod[iface] != nextNod[iface] )
4655         {
4656           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4657           polyedre_nodes.push_back( nextNod[iface] );
4658         }
4659         if ( prevNod[inextface] != nextNod[inextface] )
4660         {
4661           polyedre_nodes.push_back( nextNod[inextface] );
4662           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4663         }
4664         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4665         if ( nbFaceNodes > 2 )
4666           quantities.push_back( nbFaceNodes );
4667         else // degenerated face
4668           polyedre_nodes.resize( prevNbNodes );
4669       }
4670       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4671     }
4672
4673     if ( aNewElem ) {
4674       newElems.push_back( aNewElem );
4675       myLastCreatedElems.Append(aNewElem);
4676       srcElements.Append( elem );
4677     }
4678
4679     // set new prev nodes
4680     for ( iNode = 0; iNode < nbNodes; iNode++ )
4681       prevNod[ iNode ] = nextNod[ iNode ];
4682
4683   } // for steps
4684 }
4685
4686 //=======================================================================
4687 /*!
4688  * \brief Create 1D and 2D elements around swept elements
4689  * \param mapNewNodes - source nodes and ones generated from them
4690  * \param newElemsMap - source elements and ones generated from them
4691  * \param elemNewNodesMap - nodes generated from each node of each element
4692  * \param elemSet - all swept elements
4693  * \param nbSteps - number of sweeping steps
4694  * \param srcElements - to append elem for each generated element
4695  */
4696 //=======================================================================
4697
4698 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4699                                   TTElemOfElemListMap &    newElemsMap,
4700                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4701                                   TIDSortedElemSet&        elemSet,
4702                                   const int                nbSteps,
4703                                   SMESH_SequenceOfElemPtr& srcElements)
4704 {
4705   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4706   SMESHDS_Mesh* aMesh = GetMeshDS();
4707
4708   // Find nodes belonging to only one initial element - sweep them into edges.
4709
4710   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4711   for ( ; nList != mapNewNodes.end(); nList++ )
4712   {
4713     const SMDS_MeshNode* node =
4714       static_cast<const SMDS_MeshNode*>( nList->first );
4715     if ( newElemsMap.count( node ))
4716       continue; // node was extruded into edge
4717     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4718     int nbInitElems = 0;
4719     const SMDS_MeshElement* el = 0;
4720     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4721     while ( eIt->more() && nbInitElems < 2 ) {
4722       el = eIt->next();
4723       SMDSAbs_ElementType type = el->GetType();
4724       if ( type == SMDSAbs_Volume || type < highType ) continue;
4725       if ( type > highType ) {
4726         nbInitElems = 0;
4727         highType = type;
4728       }
4729       nbInitElems += elemSet.count(el);
4730     }
4731     if ( nbInitElems < 2 ) {
4732       bool NotCreateEdge = el && el->IsMediumNode(node);
4733       if(!NotCreateEdge) {
4734         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4735         list<const SMDS_MeshElement*> newEdges;
4736         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4737       }
4738     }
4739   }
4740
4741   // Make a ceiling for each element ie an equal element of last new nodes.
4742   // Find free links of faces - make edges and sweep them into faces.
4743
4744   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4745   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4746   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4747   {
4748     const SMDS_MeshElement* elem = itElem->first;
4749     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4750
4751     if(itElem->second.size()==0) continue;
4752
4753     const bool isQuadratic = elem->IsQuadratic();
4754
4755     if ( elem->GetType() == SMDSAbs_Edge ) {
4756       // create a ceiling edge
4757       if ( !isQuadratic ) {
4758         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4759                                vecNewNodes[ 1 ]->second.back())) {
4760           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4761                                                    vecNewNodes[ 1 ]->second.back()));
4762           srcElements.Append( elem );
4763         }
4764       }
4765       else {
4766         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4767                                vecNewNodes[ 1 ]->second.back(),
4768                                vecNewNodes[ 2 ]->second.back())) {
4769           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4770                                                    vecNewNodes[ 1 ]->second.back(),
4771                                                    vecNewNodes[ 2 ]->second.back()));
4772           srcElements.Append( elem );
4773         }
4774       }
4775     }
4776     if ( elem->GetType() != SMDSAbs_Face )
4777       continue;
4778
4779     bool hasFreeLinks = false;
4780
4781     TIDSortedElemSet avoidSet;
4782     avoidSet.insert( elem );
4783
4784     set<const SMDS_MeshNode*> aFaceLastNodes;
4785     int iNode, nbNodes = vecNewNodes.size();
4786     if ( !isQuadratic ) {
4787       // loop on the face nodes
4788       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4789         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4790         // look for free links of the face
4791         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4792         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4793         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4794         // check if a link n1-n2 is free
4795         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4796           hasFreeLinks = true;
4797           // make a new edge and a ceiling for a new edge
4798           const SMDS_MeshElement* edge;
4799           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4800             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4801             srcElements.Append( myLastCreatedElems.Last() );
4802           }
4803           n1 = vecNewNodes[ iNode ]->second.back();
4804           n2 = vecNewNodes[ iNext ]->second.back();
4805           if ( !aMesh->FindEdge( n1, n2 )) {
4806             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4807             srcElements.Append( edge );
4808           }
4809         }
4810       }
4811     }
4812     else { // elem is quadratic face
4813       int nbn = nbNodes/2;
4814       for ( iNode = 0; iNode < nbn; iNode++ ) {
4815         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4816         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4817         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4818         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4819         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4820         // check if a link is free
4821         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4822              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4823              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4824           hasFreeLinks = true;
4825           // make an edge and a ceiling for a new edge
4826           // find medium node
4827           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4828             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4829             srcElements.Append( elem );
4830           }
4831           n1 = vecNewNodes[ iNode ]->second.back();
4832           n2 = vecNewNodes[ iNext ]->second.back();
4833           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4834           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4835             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4836             srcElements.Append( elem );
4837           }
4838         }
4839       }
4840       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4841         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4842       }
4843     }
4844
4845     // sweep free links into faces
4846
4847     if ( hasFreeLinks )  {
4848       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4849       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4850
4851       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4852       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4853       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4854         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4855         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4856       }
4857       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4858         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4859         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4860       }
4861       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4862         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4863         std::advance( v, volNb );
4864         // find indices of free faces of a volume and their source edges
4865         list< int > freeInd;
4866         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4867         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4868         int iF, nbF = vTool.NbFaces();
4869         for ( iF = 0; iF < nbF; iF ++ ) {
4870           if (vTool.IsFreeFace( iF ) &&
4871               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4872               initNodeSet != faceNodeSet) // except an initial face
4873           {
4874             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4875               continue;
4876             if ( faceNodeSet == initNodeSetNoCenter )
4877               continue;
4878             freeInd.push_back( iF );
4879             // find source edge of a free face iF
4880             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4881             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4882             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4883                                    initNodeSet.begin(), initNodeSet.end(),
4884                                    commonNodes.begin());
4885             if ( (*v)->IsQuadratic() )
4886               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4887             else
4888               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4889 #ifdef _DEBUG_
4890             if ( !srcEdges.back() )
4891             {
4892               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4893                    << iF << " of volume #" << vTool.ID() << endl;
4894             }
4895 #endif
4896           }
4897         }
4898         if ( freeInd.empty() )
4899           continue;
4900
4901         // create faces for all steps;
4902         // if such a face has been already created by sweep of edge,
4903         // assure that its orientation is OK
4904         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4905           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4906           vTool.SetExternalNormal();
4907           const int nextShift = vTool.IsForward() ? +1 : -1;
4908           list< int >::iterator ind = freeInd.begin();
4909           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4910           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4911           {
4912             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4913             int nbn = vTool.NbFaceNodes( *ind );
4914             const SMDS_MeshElement * f = 0;
4915             if ( nbn == 3 )              ///// triangle
4916             {
4917               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4918               if ( !f ||
4919                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4920               {
4921                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4922                                                      nodes[ 1 ],
4923                                                      nodes[ 1 + nextShift ] };
4924                 if ( f )
4925                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4926                 else
4927                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4928                                                             newOrder[ 2 ] ));
4929               }
4930             }
4931             else if ( nbn == 4 )       ///// quadrangle
4932             {
4933               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4934               if ( !f ||
4935                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4936               {
4937                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4938                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4939                 if ( f )
4940                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4941                 else
4942                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4943                                                             newOrder[ 2 ], newOrder[ 3 ]));
4944               }
4945             }
4946             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4947             {
4948               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4949               if ( !f ||
4950                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4951               {
4952                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4953                                                      nodes[2],
4954                                                      nodes[2 + 2*nextShift],
4955                                                      nodes[3 - 2*nextShift],
4956                                                      nodes[3],
4957                                                      nodes[3 + 2*nextShift]};
4958                 if ( f )
4959                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4960                 else
4961                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4962                                                             newOrder[ 1 ],
4963                                                             newOrder[ 2 ],
4964                                                             newOrder[ 3 ],
4965                                                             newOrder[ 4 ],
4966                                                             newOrder[ 5 ] ));
4967               }
4968             }
4969             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4970             {
4971               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4972                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4973               if ( !f ||
4974                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4975               {
4976                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4977                                                      nodes[4 - 2*nextShift],
4978                                                      nodes[4],
4979                                                      nodes[4 + 2*nextShift],
4980                                                      nodes[1],
4981                                                      nodes[5 - 2*nextShift],
4982                                                      nodes[5],
4983                                                      nodes[5 + 2*nextShift] };
4984                 if ( f )
4985                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4986                 else
4987                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4988                                                            newOrder[ 2 ], newOrder[ 3 ],
4989                                                            newOrder[ 4 ], newOrder[ 5 ],
4990                                                            newOrder[ 6 ], newOrder[ 7 ]));
4991               }
4992             }
4993             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4994             {
4995               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4996                                       SMDSAbs_Face, /*noMedium=*/false);
4997               if ( !f ||
4998                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4999               {
5000                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5001                                                      nodes[4 - 2*nextShift],
5002                                                      nodes[4],
5003                                                      nodes[4 + 2*nextShift],
5004                                                      nodes[1],
5005                                                      nodes[5 - 2*nextShift],
5006                                                      nodes[5],
5007                                                      nodes[5 + 2*nextShift],
5008                                                      nodes[8] };
5009                 if ( f )
5010                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5011                 else
5012                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5013                                                            newOrder[ 2 ], newOrder[ 3 ],
5014                                                            newOrder[ 4 ], newOrder[ 5 ],
5015                                                            newOrder[ 6 ], newOrder[ 7 ],
5016                                                            newOrder[ 8 ]));
5017               }
5018             }
5019             else  //////// polygon
5020             {
5021               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5022               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5023               if ( !f ||
5024                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5025               {
5026                 if ( !vTool.IsForward() )
5027                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5028                 if ( f )
5029                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5030                 else
5031                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5032               }
5033             }
5034
5035             while ( srcElements.Length() < myLastCreatedElems.Length() )
5036               srcElements.Append( *srcEdge );
5037
5038           }  // loop on free faces
5039
5040           // go to the next volume
5041           iVol = 0;
5042           while ( iVol++ < nbVolumesByStep ) v++;
5043
5044         } // loop on steps
5045       } // loop on volumes of one step
5046     } // sweep free links into faces
5047
5048     // Make a ceiling face with a normal external to a volume
5049
5050     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5051     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5052     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5053
5054     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5055       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5056       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5057     }
5058     if ( iF >= 0 ) {
5059       lastVol.SetExternalNormal();
5060       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5061       int nbn = lastVol.NbFaceNodes( iF );
5062       // we do not use this->AddElement() because nodes are interlaced
5063       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5064       if ( !hasFreeLinks ||
5065            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5066       {
5067         if ( nbn == 3 )
5068           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5069
5070         else if ( nbn == 4 )
5071           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5072
5073         else if ( nbn == 6 && isQuadratic )
5074           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5075                                                     nodes[1], nodes[3], nodes[5]));
5076         else if ( nbn == 7 && isQuadratic )
5077           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5078                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
5079         else if ( nbn == 8 && isQuadratic )
5080           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5081                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5082         else if ( nbn == 9 && isQuadratic )
5083           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5084                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5085                                                     nodes[8]));
5086         else
5087           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5088
5089         while ( srcElements.Length() < myLastCreatedElems.Length() )
5090           srcElements.Append( elem );
5091       }
5092     }
5093   } // loop on swept elements
5094 }
5095
5096 //=======================================================================
5097 //function : RotationSweep
5098 //purpose  :
5099 //=======================================================================
5100
5101 SMESH_MeshEditor::PGroupIDs
5102 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5103                                 const gp_Ax1&      theAxis,
5104                                 const double       theAngle,
5105                                 const int          theNbSteps,
5106                                 const double       theTol,
5107                                 const bool         theMakeGroups,
5108                                 const bool         theMakeWalls)
5109 {
5110   myLastCreatedElems.Clear();
5111   myLastCreatedNodes.Clear();
5112
5113   // source elements for each generated one
5114   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5115
5116   MESSAGE( "RotationSweep()");
5117   gp_Trsf aTrsf;
5118   aTrsf.SetRotation( theAxis, theAngle );
5119   gp_Trsf aTrsf2;
5120   aTrsf2.SetRotation( theAxis, theAngle/2. );
5121
5122   gp_Lin aLine( theAxis );
5123   double aSqTol = theTol * theTol;
5124
5125   SMESHDS_Mesh* aMesh = GetMeshDS();
5126
5127   TNodeOfNodeListMap mapNewNodes;
5128   TElemOfVecOfNnlmiMap mapElemNewNodes;
5129   TTElemOfElemListMap newElemsMap;
5130
5131   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5132                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5133                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5134   // loop on theElems
5135   TIDSortedElemSet::iterator itElem;
5136   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5137     const SMDS_MeshElement* elem = *itElem;
5138     if ( !elem || elem->GetType() == SMDSAbs_Volume )
5139       continue;
5140     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5141     newNodesItVec.reserve( elem->NbNodes() );
5142
5143     // loop on elem nodes
5144     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5145     while ( itN->more() )
5146     {
5147       // check if a node has been already sweeped
5148       const SMDS_MeshNode* node = cast2Node( itN->next() );
5149
5150       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5151       double coord[3];
5152       aXYZ.Coord( coord[0], coord[1], coord[2] );
5153       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5154
5155       TNodeOfNodeListMapItr nIt =
5156         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5157       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5158       if ( listNewNodes.empty() )
5159       {
5160         // check if we are to create medium nodes between corner ones
5161         bool needMediumNodes = false;
5162         if ( isQuadraticMesh )
5163         {
5164           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5165           while (it->more() && !needMediumNodes )
5166           {
5167             const SMDS_MeshElement* invElem = it->next();
5168             if ( invElem != elem && !theElems.count( invElem )) continue;
5169             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5170             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5171               needMediumNodes = true;
5172           }
5173         }
5174
5175         // make new nodes
5176         const SMDS_MeshNode * newNode = node;
5177         for ( int i = 0; i < theNbSteps; i++ ) {
5178           if ( !isOnAxis ) {
5179             if ( needMediumNodes )  // create a medium node
5180             {
5181               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5182               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5183               myLastCreatedNodes.Append(newNode);
5184               srcNodes.Append( node );
5185               listNewNodes.push_back( newNode );
5186               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5187             }
5188             else {
5189               aTrsf.Transforms( coord[0], coord[1], coord[2] );
5190             }
5191             // create a corner node
5192             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5193             myLastCreatedNodes.Append(newNode);
5194             srcNodes.Append( node );
5195             listNewNodes.push_back( newNode );
5196           }
5197           else {
5198             listNewNodes.push_back( newNode );
5199             // if ( needMediumNodes )
5200             //   listNewNodes.push_back( newNode );
5201           }
5202         }
5203       }
5204       newNodesItVec.push_back( nIt );
5205     }
5206     // make new elements
5207     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5208   }
5209
5210   if ( theMakeWalls )
5211     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5212
5213   PGroupIDs newGroupIDs;
5214   if ( theMakeGroups )
5215     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5216
5217   return newGroupIDs;
5218 }
5219
5220
5221 //=======================================================================
5222 //function : CreateNode
5223 //purpose  :
5224 //=======================================================================
5225 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5226                                                   const double y,
5227                                                   const double z,
5228                                                   const double tolnode,
5229                                                   SMESH_SequenceOfNode& aNodes)
5230 {
5231   // myLastCreatedElems.Clear();
5232   // myLastCreatedNodes.Clear();
5233
5234   gp_Pnt P1(x,y,z);
5235   SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5236
5237   // try to search in sequence of existing nodes
5238   // if aNodes.Length()>0 we 'nave to use given sequence
5239   // else - use all nodes of mesh
5240   if(aNodes.Length()>0) {
5241     int i;
5242     for(i=1; i<=aNodes.Length(); i++) {
5243       gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5244       if(P1.Distance(P2)<tolnode)
5245         return aNodes.Value(i);
5246     }
5247   }
5248   else {
5249     SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5250     while(itn->more()) {
5251       const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5252       gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5253       if(P1.Distance(P2)<tolnode)
5254         return aN;
5255     }
5256   }
5257
5258   // create new node and return it
5259   const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5260   //myLastCreatedNodes.Append(NewNode);
5261   return NewNode;
5262 }
5263
5264
5265 //=======================================================================
5266 //function : ExtrusionSweep
5267 //purpose  :
5268 //=======================================================================
5269
5270 SMESH_MeshEditor::PGroupIDs
5271 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5272                                   const gp_Vec&        theStep,
5273                                   const int            theNbSteps,
5274                                   TTElemOfElemListMap& newElemsMap,
5275                                   const bool           theMakeGroups,
5276                                   const int            theFlags,
5277                                   const double         theTolerance)
5278 {
5279   ExtrusParam aParams;
5280   aParams.myDir = gp_Dir(theStep);
5281   aParams.myNodes.Clear();
5282   aParams.mySteps = new TColStd_HSequenceOfReal;
5283   int i;
5284   for(i=1; i<=theNbSteps; i++)
5285     aParams.mySteps->Append(theStep.Magnitude());
5286
5287   return
5288     ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5289 }
5290
5291
5292 //=======================================================================
5293 //function : ExtrusionSweep
5294 //purpose  :
5295 //=======================================================================
5296
5297 SMESH_MeshEditor::PGroupIDs
5298 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5299                                   ExtrusParam&         theParams,
5300                                   TTElemOfElemListMap& newElemsMap,
5301                                   const bool           theMakeGroups,
5302                                   const int            theFlags,
5303                                   const double         theTolerance)
5304 {
5305   myLastCreatedElems.Clear();
5306   myLastCreatedNodes.Clear();
5307
5308   // source elements for each generated one
5309   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5310
5311   SMESHDS_Mesh* aMesh = GetMeshDS();
5312
5313   int nbsteps = theParams.mySteps->Length();
5314
5315   TNodeOfNodeListMap mapNewNodes;
5316   //TNodeOfNodeVecMap mapNewNodes;
5317   TElemOfVecOfNnlmiMap mapElemNewNodes;
5318   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5319
5320   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5321                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5322                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5323   // loop on theElems
5324   TIDSortedElemSet::iterator itElem;
5325   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5326     // check element type
5327     const SMDS_MeshElement* elem = *itElem;
5328     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5329       continue;
5330
5331     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5332     newNodesItVec.reserve( elem->NbNodes() );
5333
5334     // loop on elem nodes
5335     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5336     while ( itN->more() )
5337     {
5338       // check if a node has been already sweeped
5339       const SMDS_MeshNode* node = cast2Node( itN->next() );
5340       TNodeOfNodeListMap::iterator nIt =
5341         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5342       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5343       if ( listNewNodes.empty() )
5344       {
5345         // make new nodes
5346
5347         // check if we are to create medium nodes between corner ones
5348         bool needMediumNodes = false;
5349         if ( isQuadraticMesh )
5350         {
5351           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5352           while (it->more() && !needMediumNodes )
5353           {
5354             const SMDS_MeshElement* invElem = it->next();
5355             if ( invElem != elem && !theElems.count( invElem )) continue;
5356             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5357             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5358               needMediumNodes = true;
5359           }
5360         }
5361
5362         double coord[] = { node->X(), node->Y(), node->Z() };
5363         for ( int i = 0; i < nbsteps; i++ )
5364         {
5365           if ( needMediumNodes ) // create a medium node
5366           {
5367             double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5368             double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5369             double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5370             if( theFlags & EXTRUSION_FLAG_SEW ) {
5371               const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5372                                                          theTolerance, theParams.myNodes);
5373               listNewNodes.push_back( newNode );
5374             }
5375             else {
5376               const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5377               myLastCreatedNodes.Append(newNode);
5378               srcNodes.Append( node );
5379               listNewNodes.push_back( newNode );
5380             }
5381           }
5382           // create a corner node
5383           coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5384           coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5385           coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5386           if( theFlags & EXTRUSION_FLAG_SEW ) {
5387             const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5388                                                        theTolerance, theParams.myNodes);
5389             listNewNodes.push_back( newNode );
5390           }
5391           else {
5392             const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5393             myLastCreatedNodes.Append(newNode);
5394             srcNodes.Append( node );
5395             listNewNodes.push_back( newNode );
5396           }
5397         }
5398       }
5399       newNodesItVec.push_back( nIt );
5400     }
5401     // make new elements
5402     sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5403   }
5404
5405   if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5406     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5407   }
5408   PGroupIDs newGroupIDs;
5409   if ( theMakeGroups )
5410     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5411
5412   return newGroupIDs;
5413 }
5414
5415 //=======================================================================
5416 //function : ExtrusionAlongTrack
5417 //purpose  :
5418 //=======================================================================
5419 SMESH_MeshEditor::Extrusion_Error
5420 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5421                                        SMESH_subMesh*       theTrack,
5422                                        const SMDS_MeshNode* theN1,
5423                                        const bool           theHasAngles,
5424                                        list<double>&        theAngles,
5425                                        const bool           theLinearVariation,
5426                                        const bool           theHasRefPoint,
5427                                        const gp_Pnt&        theRefPoint,
5428                                        const bool           theMakeGroups)
5429 {
5430   MESSAGE("ExtrusionAlongTrack");
5431   myLastCreatedElems.Clear();
5432   myLastCreatedNodes.Clear();
5433
5434   int aNbE;
5435   std::list<double> aPrms;
5436   TIDSortedElemSet::iterator itElem;
5437
5438   gp_XYZ aGC;
5439   TopoDS_Edge aTrackEdge;
5440   TopoDS_Vertex aV1, aV2;
5441
5442   SMDS_ElemIteratorPtr aItE;
5443   SMDS_NodeIteratorPtr aItN;
5444   SMDSAbs_ElementType aTypeE;
5445
5446   TNodeOfNodeListMap mapNewNodes;
5447
5448   // 1. Check data
5449   aNbE = theElements.size();
5450   // nothing to do
5451   if ( !aNbE )
5452     return EXTR_NO_ELEMENTS;
5453
5454   // 1.1 Track Pattern
5455   ASSERT( theTrack );
5456
5457   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5458
5459   aItE = pSubMeshDS->GetElements();
5460   while ( aItE->more() ) {
5461     const SMDS_MeshElement* pE = aItE->next();
5462     aTypeE = pE->GetType();
5463     // Pattern must contain links only
5464     if ( aTypeE != SMDSAbs_Edge )
5465       return EXTR_PATH_NOT_EDGE;
5466   }
5467
5468   list<SMESH_MeshEditor_PathPoint> fullList;
5469
5470   const TopoDS_Shape& aS = theTrack->GetSubShape();
5471   // Sub-shape for the Pattern must be an Edge or Wire
5472   if( aS.ShapeType() == TopAbs_EDGE ) {
5473     aTrackEdge = TopoDS::Edge( aS );
5474     // the Edge must not be degenerated
5475     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5476       return EXTR_BAD_PATH_SHAPE;
5477     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5478     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5479     const SMDS_MeshNode* aN1 = aItN->next();
5480     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5481     const SMDS_MeshNode* aN2 = aItN->next();
5482     // starting node must be aN1 or aN2
5483     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5484       return EXTR_BAD_STARTING_NODE;
5485     aItN = pSubMeshDS->GetNodes();
5486     while ( aItN->more() ) {
5487       const SMDS_MeshNode* pNode = aItN->next();
5488       const SMDS_EdgePosition* pEPos =
5489         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5490       double aT = pEPos->GetUParameter();
5491       aPrms.push_back( aT );
5492     }
5493     //Extrusion_Error err =
5494     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5495   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5496     list< SMESH_subMesh* > LSM;
5497     TopTools_SequenceOfShape Edges;
5498     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5499     while(itSM->more()) {
5500       SMESH_subMesh* SM = itSM->next();
5501       LSM.push_back(SM);
5502       const TopoDS_Shape& aS = SM->GetSubShape();
5503       Edges.Append(aS);
5504     }
5505     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5506     int startNid = theN1->GetID();
5507     TColStd_MapOfInteger UsedNums;
5508
5509     int NbEdges = Edges.Length();
5510     int i = 1;
5511     for(; i<=NbEdges; i++) {
5512       int k = 0;
5513       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5514       for(; itLSM!=LSM.end(); itLSM++) {
5515         k++;
5516         if(UsedNums.Contains(k)) continue;
5517         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5518         SMESH_subMesh* locTrack = *itLSM;
5519         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5520         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5521         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5522         const SMDS_MeshNode* aN1 = aItN->next();
5523         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5524         const SMDS_MeshNode* aN2 = aItN->next();
5525         // starting node must be aN1 or aN2
5526         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5527         // 2. Collect parameters on the track edge
5528         aPrms.clear();
5529         aItN = locMeshDS->GetNodes();
5530         while ( aItN->more() ) {
5531           const SMDS_MeshNode* pNode = aItN->next();
5532           const SMDS_EdgePosition* pEPos =
5533             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5534           double aT = pEPos->GetUParameter();
5535           aPrms.push_back( aT );
5536         }
5537         list<SMESH_MeshEditor_PathPoint> LPP;
5538         //Extrusion_Error err =
5539         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5540         LLPPs.push_back(LPP);
5541         UsedNums.Add(k);
5542         // update startN for search following egde
5543         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5544         else startNid = aN1->GetID();
5545         break;
5546       }
5547     }
5548     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5549     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5550     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5551     for(; itPP!=firstList.end(); itPP++) {
5552       fullList.push_back( *itPP );
5553     }
5554     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5555     fullList.pop_back();
5556     itLLPP++;
5557     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5558       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5559       itPP = currList.begin();
5560       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5561       gp_Dir D1 = PP1.Tangent();
5562       gp_Dir D2 = PP2.Tangent();
5563       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5564                            (D1.Z()+D2.Z())/2 ) );
5565       PP1.SetTangent(Dnew);
5566       fullList.push_back(PP1);
5567       itPP++;
5568       for(; itPP!=firstList.end(); itPP++) {
5569         fullList.push_back( *itPP );
5570       }
5571       PP1 = fullList.back();
5572       fullList.pop_back();
5573     }
5574     // if wire not closed
5575     fullList.push_back(PP1);
5576     // else ???
5577   }
5578   else {
5579     return EXTR_BAD_PATH_SHAPE;
5580   }
5581
5582   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5583                           theHasRefPoint, theRefPoint, theMakeGroups);
5584 }
5585
5586
5587 //=======================================================================
5588 //function : ExtrusionAlongTrack
5589 //purpose  :
5590 //=======================================================================
5591 SMESH_MeshEditor::Extrusion_Error
5592 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5593                                        SMESH_Mesh*          theTrack,
5594                                        const SMDS_MeshNode* theN1,
5595                                        const bool           theHasAngles,
5596                                        list<double>&        theAngles,
5597                                        const bool           theLinearVariation,
5598                                        const bool           theHasRefPoint,
5599                                        const gp_Pnt&        theRefPoint,
5600                                        const bool           theMakeGroups)
5601 {
5602   myLastCreatedElems.Clear();
5603   myLastCreatedNodes.Clear();
5604
5605   int aNbE;
5606   std::list<double> aPrms;
5607   TIDSortedElemSet::iterator itElem;
5608
5609   gp_XYZ aGC;
5610   TopoDS_Edge aTrackEdge;
5611   TopoDS_Vertex aV1, aV2;
5612
5613   SMDS_ElemIteratorPtr aItE;
5614   SMDS_NodeIteratorPtr aItN;
5615   SMDSAbs_ElementType aTypeE;
5616
5617   TNodeOfNodeListMap mapNewNodes;
5618
5619   // 1. Check data
5620   aNbE = theElements.size();
5621   // nothing to do
5622   if ( !aNbE )
5623     return EXTR_NO_ELEMENTS;
5624
5625   // 1.1 Track Pattern
5626   ASSERT( theTrack );
5627
5628   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5629
5630   aItE = pMeshDS->elementsIterator();
5631   while ( aItE->more() ) {
5632     const SMDS_MeshElement* pE = aItE->next();
5633     aTypeE = pE->GetType();
5634     // Pattern must contain links only
5635     if ( aTypeE != SMDSAbs_Edge )
5636       return EXTR_PATH_NOT_EDGE;
5637   }
5638
5639   list<SMESH_MeshEditor_PathPoint> fullList;
5640
5641   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5642
5643   if ( !theTrack->HasShapeToMesh() ) {
5644     //Mesh without shape
5645     const SMDS_MeshNode* currentNode = NULL;
5646     const SMDS_MeshNode* prevNode = theN1;
5647     std::vector<const SMDS_MeshNode*> aNodesList;
5648     aNodesList.push_back(theN1);
5649     int nbEdges = 0, conn=0;
5650     const SMDS_MeshElement* prevElem = NULL;
5651     const SMDS_MeshElement* currentElem = NULL;
5652     int totalNbEdges = theTrack->NbEdges();
5653     SMDS_ElemIteratorPtr nIt;
5654
5655     //check start node
5656     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5657       return EXTR_BAD_STARTING_NODE;
5658     }
5659
5660     conn = nbEdgeConnectivity(theN1);
5661     if(conn > 2)
5662       return EXTR_PATH_NOT_EDGE;
5663
5664     aItE = theN1->GetInverseElementIterator();
5665     prevElem = aItE->next();
5666     currentElem = prevElem;
5667     //Get all nodes
5668     if(totalNbEdges == 1 ) {
5669       nIt = currentElem->nodesIterator();
5670       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5671       if(currentNode == prevNode)
5672         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5673       aNodesList.push_back(currentNode);
5674     } else {
5675       nIt = currentElem->nodesIterator();
5676       while( nIt->more() ) {
5677         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5678         if(currentNode == prevNode)
5679           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5680         aNodesList.push_back(currentNode);
5681
5682         //case of the closed mesh
5683         if(currentNode == theN1) {
5684           nbEdges++;
5685           break;
5686         }
5687
5688         conn = nbEdgeConnectivity(currentNode);
5689         if(conn > 2) {
5690           return EXTR_PATH_NOT_EDGE;
5691         }else if( conn == 1 && nbEdges > 0 ) {
5692           //End of the path
5693           nbEdges++;
5694           break;
5695         }else {
5696           prevNode = currentNode;
5697           aItE = currentNode->GetInverseElementIterator();
5698           currentElem = aItE->next();
5699           if( currentElem  == prevElem)
5700             currentElem = aItE->next();
5701           nIt = currentElem->nodesIterator();
5702           prevElem = currentElem;
5703           nbEdges++;
5704         }
5705       }
5706     }
5707
5708     if(nbEdges != totalNbEdges)
5709       return EXTR_PATH_NOT_EDGE;
5710
5711     TopTools_SequenceOfShape Edges;
5712     double x1,x2,y1,y2,z1,z2;
5713     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5714     int startNid = theN1->GetID();
5715     for(int i = 1; i < aNodesList.size(); i++) {
5716       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5717       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5718       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5719       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5720       list<SMESH_MeshEditor_PathPoint> LPP;
5721       aPrms.clear();
5722       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5723       LLPPs.push_back(LPP);
5724       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5725       else startNid = aNodesList[i-1]->GetID();
5726
5727     }
5728
5729     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5730     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5731     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5732     for(; itPP!=firstList.end(); itPP++) {
5733       fullList.push_back( *itPP );
5734     }
5735
5736     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5737     SMESH_MeshEditor_PathPoint PP2;
5738     fullList.pop_back();
5739     itLLPP++;
5740     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5741       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5742       itPP = currList.begin();
5743       PP2 = currList.front();
5744       gp_Dir D1 = PP1.Tangent();
5745       gp_Dir D2 = PP2.Tangent();
5746       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5747                            (D1.Z()+D2.Z())/2 ) );
5748       PP1.SetTangent(Dnew);
5749       fullList.push_back(PP1);
5750       itPP++;
5751       for(; itPP!=currList.end(); itPP++) {
5752         fullList.push_back( *itPP );
5753       }
5754       PP1 = fullList.back();
5755       fullList.pop_back();
5756     }
5757     fullList.push_back(PP1);
5758
5759   } // Sub-shape for the Pattern must be an Edge or Wire
5760   else if( aS.ShapeType() == TopAbs_EDGE ) {
5761     aTrackEdge = TopoDS::Edge( aS );
5762     // the Edge must not be degenerated
5763     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5764       return EXTR_BAD_PATH_SHAPE;
5765     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5766     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5767     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5768     // starting node must be aN1 or aN2
5769     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5770       return EXTR_BAD_STARTING_NODE;
5771     aItN = pMeshDS->nodesIterator();
5772     while ( aItN->more() ) {
5773       const SMDS_MeshNode* pNode = aItN->next();
5774       if( pNode==aN1 || pNode==aN2 ) continue;
5775       const SMDS_EdgePosition* pEPos =
5776         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5777       double aT = pEPos->GetUParameter();
5778       aPrms.push_back( aT );
5779     }
5780     //Extrusion_Error err =
5781     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5782   }
5783   else if( aS.ShapeType() == TopAbs_WIRE ) {
5784     list< SMESH_subMesh* > LSM;
5785     TopTools_SequenceOfShape Edges;
5786     TopExp_Explorer eExp(aS, TopAbs_EDGE);
5787     for(; eExp.More(); eExp.Next()) {
5788       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5789       if( SMESH_Algo::isDegenerated(E) ) continue;
5790       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5791       if(SM) {
5792         LSM.push_back(SM);
5793         Edges.Append(E);
5794       }
5795     }
5796     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5797     TopoDS_Vertex aVprev;
5798     TColStd_MapOfInteger UsedNums;
5799     int NbEdges = Edges.Length();
5800     int i = 1;
5801     for(; i<=NbEdges; i++) {
5802       int k = 0;
5803       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5804       for(; itLSM!=LSM.end(); itLSM++) {
5805         k++;
5806         if(UsedNums.Contains(k)) continue;
5807         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5808         SMESH_subMesh* locTrack = *itLSM;
5809         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5810         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5811         bool aN1isOK = false, aN2isOK = false;
5812         if ( aVprev.IsNull() ) {
5813           // if previous vertex is not yet defined, it means that we in the beginning of wire
5814           // and we have to find initial vertex corresponding to starting node theN1
5815           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5816           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5817           // starting node must be aN1 or aN2
5818           aN1isOK = ( aN1 && aN1 == theN1 );
5819           aN2isOK = ( aN2 && aN2 == theN1 );
5820         }
5821         else {
5822           // we have specified ending vertex of the previous edge on the previous iteration
5823           // and we have just to check that it corresponds to any vertex in current segment
5824           aN1isOK = aVprev.IsSame( aV1 );
5825           aN2isOK = aVprev.IsSame( aV2 );
5826         }
5827         if ( !aN1isOK && !aN2isOK ) continue;
5828         // 2. Collect parameters on the track edge
5829         aPrms.clear();
5830         aItN = locMeshDS->GetNodes();
5831         while ( aItN->more() ) {
5832           const SMDS_MeshNode*     pNode = aItN->next();
5833           const SMDS_EdgePosition* pEPos =
5834             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5835           double aT = pEPos->GetUParameter();
5836           aPrms.push_back( aT );
5837         }
5838         list<SMESH_MeshEditor_PathPoint> LPP;
5839         //Extrusion_Error err =
5840         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5841         LLPPs.push_back(LPP);
5842         UsedNums.Add(k);
5843         // update startN for search following egde
5844         if ( aN1isOK ) aVprev = aV2;
5845         else           aVprev = aV1;
5846         break;
5847       }
5848     }
5849     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5850     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5851     fullList.splice( fullList.end(), firstList );
5852
5853     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5854     fullList.pop_back();
5855     itLLPP++;
5856     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5857       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5858       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5859       gp_Dir D1 = PP1.Tangent();
5860       gp_Dir D2 = PP2.Tangent();
5861       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5862       PP1.SetTangent(Dnew);
5863       fullList.push_back(PP1);
5864       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5865       PP1 = fullList.back();
5866       fullList.pop_back();
5867     }
5868     // if wire not closed
5869     fullList.push_back(PP1);
5870     // else ???
5871   }
5872   else {
5873     return EXTR_BAD_PATH_SHAPE;
5874   }
5875
5876   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5877                           theHasRefPoint, theRefPoint, theMakeGroups);
5878 }
5879
5880
5881 //=======================================================================
5882 //function : MakeEdgePathPoints
5883 //purpose  : auxilary for ExtrusionAlongTrack
5884 //=======================================================================
5885 SMESH_MeshEditor::Extrusion_Error
5886 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
5887                                      const TopoDS_Edge&                aTrackEdge,
5888                                      bool                              FirstIsStart,
5889                                      list<SMESH_MeshEditor_PathPoint>& LPP)
5890 {
5891   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5892   aTolVec=1.e-7;
5893   aTolVec2=aTolVec*aTolVec;
5894   double aT1, aT2;
5895   TopoDS_Vertex aV1, aV2;
5896   TopExp::Vertices( aTrackEdge, aV1, aV2 );
5897   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5898   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5899   // 2. Collect parameters on the track edge
5900   aPrms.push_front( aT1 );
5901   aPrms.push_back( aT2 );
5902   // sort parameters
5903   aPrms.sort();
5904   if( FirstIsStart ) {
5905     if ( aT1 > aT2 ) {
5906       aPrms.reverse();
5907     }
5908   }
5909   else {
5910     if ( aT2 > aT1 ) {
5911       aPrms.reverse();
5912     }
5913   }
5914   // 3. Path Points
5915   SMESH_MeshEditor_PathPoint aPP;
5916   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5917   std::list<double>::iterator aItD = aPrms.begin();
5918   for(; aItD != aPrms.end(); ++aItD) {
5919     double aT = *aItD;
5920     gp_Pnt aP3D;
5921     gp_Vec aVec;
5922     aC3D->D1( aT, aP3D, aVec );
5923     aL2 = aVec.SquareMagnitude();
5924     if ( aL2 < aTolVec2 )
5925       return EXTR_CANT_GET_TANGENT;
5926     gp_Dir aTgt( aVec );
5927     aPP.SetPnt( aP3D );
5928     aPP.SetTangent( aTgt );
5929     aPP.SetParameter( aT );
5930     LPP.push_back(aPP);
5931   }
5932   return EXTR_OK;
5933 }
5934
5935
5936 //=======================================================================
5937 //function : MakeExtrElements
5938 //purpose  : auxilary for ExtrusionAlongTrack
5939 //=======================================================================
5940 SMESH_MeshEditor::Extrusion_Error
5941 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&                 theElements,
5942                                    list<SMESH_MeshEditor_PathPoint>& fullList,
5943                                    const bool                        theHasAngles,
5944                                    list<double>&                     theAngles,
5945                                    const bool                        theLinearVariation,
5946                                    const bool                        theHasRefPoint,
5947                                    const gp_Pnt&                     theRefPoint,
5948                                    const bool                        theMakeGroups)
5949 {
5950   const int aNbTP = fullList.size();
5951   // Angles
5952   if( theHasAngles && !theAngles.empty() && theLinearVariation )
5953     LinearAngleVariation(aNbTP-1, theAngles);
5954   // fill vector of path points with angles
5955   vector<SMESH_MeshEditor_PathPoint> aPPs;
5956   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5957   list<double>::iterator                 itAngles = theAngles.begin();
5958   aPPs.push_back( *itPP++ );
5959   for( ; itPP != fullList.end(); itPP++) {
5960     aPPs.push_back( *itPP );
5961     if ( theHasAngles && itAngles != theAngles.end() )
5962       aPPs.back().SetAngle( *itAngles++ );
5963   }
5964
5965   TNodeOfNodeListMap   mapNewNodes;
5966   TElemOfVecOfNnlmiMap mapElemNewNodes;
5967   TTElemOfElemListMap  newElemsMap;
5968   TIDSortedElemSet::iterator itElem;
5969   // source elements for each generated one
5970   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5971
5972   // 3. Center of rotation aV0
5973   gp_Pnt aV0 = theRefPoint;
5974   if ( !theHasRefPoint )
5975   {
5976     gp_XYZ aGC( 0.,0.,0. );
5977     TIDSortedElemSet newNodes;
5978
5979     itElem = theElements.begin();
5980     for ( ; itElem != theElements.end(); itElem++ ) {
5981       const SMDS_MeshElement* elem = *itElem;
5982
5983       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5984       while ( itN->more() ) {
5985         const SMDS_MeshElement* node = itN->next();
5986         if ( newNodes.insert( node ).second )
5987           aGC += SMESH_TNodeXYZ( node );
5988       }
5989     }
5990     aGC /= newNodes.size();
5991     aV0.SetXYZ( aGC );
5992   } // if (!theHasRefPoint) {
5993
5994   // 4. Processing the elements
5995   SMESHDS_Mesh* aMesh = GetMeshDS();
5996
5997   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5998     // check element type
5999     const SMDS_MeshElement* elem = *itElem;
6000     SMDSAbs_ElementType   aTypeE = elem->GetType();
6001     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
6002       continue;
6003
6004     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6005     newNodesItVec.reserve( elem->NbNodes() );
6006
6007     // loop on elem nodes
6008     int nodeIndex = -1;
6009     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6010     while ( itN->more() )
6011     {
6012       ++nodeIndex;
6013       // check if a node has been already processed
6014       const SMDS_MeshNode* node =
6015         static_cast<const SMDS_MeshNode*>( itN->next() );
6016       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6017       if ( nIt == mapNewNodes.end() ) {
6018         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6019         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6020
6021         // make new nodes
6022         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6023         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6024         gp_Ax1 anAx1, anAxT1T0;
6025         gp_Dir aDT1x, aDT0x, aDT1T0;
6026
6027         aTolAng=1.e-4;
6028
6029         aV0x = aV0;
6030         aPN0 = SMESH_TNodeXYZ( node );
6031
6032         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6033         aP0x = aPP0.Pnt();
6034         aDT0x= aPP0.Tangent();
6035         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6036
6037         for ( int j = 1; j < aNbTP; ++j ) {
6038           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6039           aP1x     = aPP1.Pnt();
6040           aDT1x    = aPP1.Tangent();
6041           aAngle1x = aPP1.Angle();
6042
6043           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6044           // Translation
6045           gp_Vec aV01x( aP0x, aP1x );
6046           aTrsf.SetTranslation( aV01x );
6047
6048           // traslated point
6049           aV1x = aV0x.Transformed( aTrsf );
6050           aPN1 = aPN0.Transformed( aTrsf );
6051
6052           // rotation 1 [ T1,T0 ]
6053           aAngleT1T0=-aDT1x.Angle( aDT0x );
6054           if (fabs(aAngleT1T0) > aTolAng) {
6055             aDT1T0=aDT1x^aDT0x;
6056             anAxT1T0.SetLocation( aV1x );
6057             anAxT1T0.SetDirection( aDT1T0 );
6058             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6059
6060             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6061           }
6062
6063           // rotation 2
6064           if ( theHasAngles ) {
6065             anAx1.SetLocation( aV1x );
6066             anAx1.SetDirection( aDT1x );
6067             aTrsfRot.SetRotation( anAx1, aAngle1x );
6068
6069             aPN1 = aPN1.Transformed( aTrsfRot );
6070           }
6071
6072           // make new node
6073           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6074           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6075             // create additional node
6076             double x = ( aPN1.X() + aPN0.X() )/2.;
6077             double y = ( aPN1.Y() + aPN0.Y() )/2.;
6078             double z = ( aPN1.Z() + aPN0.Z() )/2.;
6079             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6080             myLastCreatedNodes.Append(newNode);
6081             srcNodes.Append( node );
6082             listNewNodes.push_back( newNode );
6083           }
6084           const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6085           myLastCreatedNodes.Append(newNode);
6086           srcNodes.Append( node );
6087           listNewNodes.push_back( newNode );
6088
6089           aPN0 = aPN1;
6090           aP0x = aP1x;
6091           aV0x = aV1x;
6092           aDT0x = aDT1x;
6093         }
6094       }
6095
6096       else {
6097         // if current elem is quadratic and current node is not medium
6098         // we have to check - may be it is needed to insert additional nodes
6099         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6100           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6101           if(listNewNodes.size()==aNbTP-1) {
6102             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6103             gp_XYZ P(node->X(), node->Y(), node->Z());
6104             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6105             int i;
6106             for(i=0; i<aNbTP-1; i++) {
6107               const SMDS_MeshNode* N = *it;
6108               double x = ( N->X() + P.X() )/2.;
6109               double y = ( N->Y() + P.Y() )/2.;
6110               double z = ( N->Z() + P.Z() )/2.;
6111               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6112               srcNodes.Append( node );
6113               myLastCreatedNodes.Append(newN);
6114               aNodes[2*i] = newN;
6115               aNodes[2*i+1] = N;
6116               P = gp_XYZ(N->X(),N->Y(),N->Z());
6117             }
6118             listNewNodes.clear();
6119             for(i=0; i<2*(aNbTP-1); i++) {
6120               listNewNodes.push_back(aNodes[i]);
6121             }
6122           }
6123         }
6124       }
6125
6126       newNodesItVec.push_back( nIt );
6127     }
6128     // make new elements
6129     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6130     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6131     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6132   }
6133
6134   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6135
6136   if ( theMakeGroups )
6137     generateGroups( srcNodes, srcElems, "extruded");
6138
6139   return EXTR_OK;
6140 }
6141
6142
6143 //=======================================================================
6144 //function : LinearAngleVariation
6145 //purpose  : auxilary for ExtrusionAlongTrack
6146 //=======================================================================
6147 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6148                                             list<double>& Angles)
6149 {
6150   int nbAngles = Angles.size();
6151   if( nbSteps > nbAngles ) {
6152     vector<double> theAngles(nbAngles);
6153     list<double>::iterator it = Angles.begin();
6154     int i = -1;
6155     for(; it!=Angles.end(); it++) {
6156       i++;
6157       theAngles[i] = (*it);
6158     }
6159     list<double> res;
6160     double rAn2St = double( nbAngles ) / double( nbSteps );
6161     double angPrev = 0, angle;
6162     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6163       double angCur = rAn2St * ( iSt+1 );
6164       double angCurFloor  = floor( angCur );
6165       double angPrevFloor = floor( angPrev );
6166       if ( angPrevFloor == angCurFloor )
6167         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6168       else {
6169         int iP = int( angPrevFloor );
6170         double angPrevCeil = ceil(angPrev);
6171         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6172
6173         int iC = int( angCurFloor );
6174         if ( iC < nbAngles )
6175           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6176
6177         iP = int( angPrevCeil );
6178         while ( iC-- > iP )
6179           angle += theAngles[ iC ];
6180       }
6181       res.push_back(angle);
6182       angPrev = angCur;
6183     }
6184     Angles.clear();
6185     it = res.begin();
6186     for(; it!=res.end(); it++)
6187       Angles.push_back( *it );
6188   }
6189 }
6190
6191
6192 //================================================================================
6193 /*!
6194  * \brief Move or copy theElements applying theTrsf to their nodes
6195  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6196  *  \param theTrsf - transformation to apply
6197  *  \param theCopy - if true, create translated copies of theElems
6198  *  \param theMakeGroups - if true and theCopy, create translated groups
6199  *  \param theTargetMesh - mesh to copy translated elements into
6200  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6201  */
6202 //================================================================================
6203
6204 SMESH_MeshEditor::PGroupIDs
6205 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6206                              const gp_Trsf&     theTrsf,
6207                              const bool         theCopy,
6208                              const bool         theMakeGroups,
6209                              SMESH_Mesh*        theTargetMesh)
6210 {
6211   myLastCreatedElems.Clear();
6212   myLastCreatedNodes.Clear();
6213
6214   bool needReverse = false;
6215   string groupPostfix;
6216   switch ( theTrsf.Form() ) {
6217   case gp_PntMirror:
6218     MESSAGE("gp_PntMirror");
6219     needReverse = true;
6220     groupPostfix = "mirrored";
6221     break;
6222   case gp_Ax1Mirror:
6223     MESSAGE("gp_Ax1Mirror");
6224     groupPostfix = "mirrored";
6225     break;
6226   case gp_Ax2Mirror:
6227     MESSAGE("gp_Ax2Mirror");
6228     needReverse = true;
6229     groupPostfix = "mirrored";
6230     break;
6231   case gp_Rotation:
6232     MESSAGE("gp_Rotation");
6233     groupPostfix = "rotated";
6234     break;
6235   case gp_Translation:
6236     MESSAGE("gp_Translation");
6237     groupPostfix = "translated";
6238     break;
6239   case gp_Scale:
6240     MESSAGE("gp_Scale");
6241     groupPostfix = "scaled";
6242     break;
6243   case gp_CompoundTrsf: // different scale by axis
6244     MESSAGE("gp_CompoundTrsf");
6245     groupPostfix = "scaled";
6246     break;
6247   default:
6248     MESSAGE("default");
6249     needReverse = false;
6250     groupPostfix = "transformed";
6251   }
6252
6253   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6254   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6255   SMESHDS_Mesh* aMesh    = GetMeshDS();
6256
6257
6258   // map old node to new one
6259   TNodeNodeMap nodeMap;
6260
6261   // elements sharing moved nodes; those of them which have all
6262   // nodes mirrored but are not in theElems are to be reversed
6263   TIDSortedElemSet inverseElemSet;
6264
6265   // source elements for each generated one
6266   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6267
6268   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6269   TIDSortedElemSet orphanNode;
6270
6271   if ( theElems.empty() ) // transform the whole mesh
6272   {
6273     // add all elements
6274     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6275     while ( eIt->more() ) theElems.insert( eIt->next() );
6276     // add orphan nodes
6277     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6278     while ( nIt->more() )
6279     {
6280       const SMDS_MeshNode* node = nIt->next();
6281       if ( node->NbInverseElements() == 0)
6282         orphanNode.insert( node );
6283     }
6284   }
6285
6286   // loop on elements to transform nodes : first orphan nodes then elems
6287   TIDSortedElemSet::iterator itElem;
6288   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6289   for (int i=0; i<2; i++)
6290   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6291     const SMDS_MeshElement* elem = *itElem;
6292     if ( !elem )
6293       continue;
6294
6295     // loop on elem nodes
6296     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6297     while ( itN->more() ) {
6298
6299       const SMDS_MeshNode* node = cast2Node( itN->next() );
6300       // check if a node has been already transformed
6301       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6302         nodeMap.insert( make_pair ( node, node ));
6303       if ( !n2n_isnew.second )
6304         continue;
6305
6306       double coord[3];
6307       coord[0] = node->X();
6308       coord[1] = node->Y();
6309       coord[2] = node->Z();
6310       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6311       if ( theTargetMesh ) {
6312         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6313         n2n_isnew.first->second = newNode;
6314         myLastCreatedNodes.Append(newNode);
6315         srcNodes.Append( node );
6316       }
6317       else if ( theCopy ) {
6318         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6319         n2n_isnew.first->second = newNode;
6320         myLastCreatedNodes.Append(newNode);
6321         srcNodes.Append( node );
6322       }
6323       else {
6324         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6325         // node position on shape becomes invalid
6326         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6327           ( SMDS_SpacePosition::originSpacePosition() );
6328       }
6329
6330       // keep inverse elements
6331       if ( !theCopy && !theTargetMesh && needReverse ) {
6332         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6333         while ( invElemIt->more() ) {
6334           const SMDS_MeshElement* iel = invElemIt->next();
6335           inverseElemSet.insert( iel );
6336         }
6337       }
6338     }
6339   }
6340
6341   // either create new elements or reverse mirrored ones
6342   if ( !theCopy && !needReverse && !theTargetMesh )
6343     return PGroupIDs();
6344
6345   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6346   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6347     theElems.insert( *invElemIt );
6348
6349   // Replicate or reverse elements
6350
6351   std::vector<int> iForw;
6352   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6353   {
6354     const SMDS_MeshElement* elem = *itElem;
6355     if ( !elem ) continue;
6356
6357     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6358     int                  nbNodes  = elem->NbNodes();
6359     if ( geomType == SMDSGeom_NONE ) continue; // node
6360
6361     switch ( geomType ) {
6362
6363     case SMDSGeom_POLYGON:  // ---------------------- polygon
6364       {
6365         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6366         int iNode = 0;
6367         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6368         while (itN->more()) {
6369           const SMDS_MeshNode* node =
6370             static_cast<const SMDS_MeshNode*>(itN->next());
6371           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6372           if (nodeMapIt == nodeMap.end())
6373             break; // not all nodes transformed
6374           if (needReverse) {
6375             // reverse mirrored faces and volumes
6376             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6377           } else {
6378             poly_nodes[iNode] = (*nodeMapIt).second;
6379           }
6380           iNode++;
6381         }
6382         if ( iNode != nbNodes )
6383           continue; // not all nodes transformed
6384
6385         if ( theTargetMesh ) {
6386           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6387           srcElems.Append( elem );
6388         }
6389         else if ( theCopy ) {
6390           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6391           srcElems.Append( elem );
6392         }
6393         else {
6394           aMesh->ChangePolygonNodes(elem, poly_nodes);
6395         }
6396       }
6397       break;
6398
6399     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6400       {
6401         const SMDS_VtkVolume* aPolyedre =
6402           dynamic_cast<const SMDS_VtkVolume*>( elem );
6403         if (!aPolyedre) {
6404           MESSAGE("Warning: bad volumic element");
6405           continue;
6406         }
6407
6408         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6409         vector<int> quantities; quantities.reserve( nbNodes );
6410
6411         bool allTransformed = true;
6412         int nbFaces = aPolyedre->NbFaces();
6413         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6414           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6415           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6416             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6417             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6418             if (nodeMapIt == nodeMap.end()) {
6419               allTransformed = false; // not all nodes transformed
6420             } else {
6421               poly_nodes.push_back((*nodeMapIt).second);
6422             }
6423             if ( needReverse && allTransformed )
6424               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6425           }
6426           quantities.push_back(nbFaceNodes);
6427         }
6428         if ( !allTransformed )
6429           continue; // not all nodes transformed
6430
6431         if ( theTargetMesh ) {
6432           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6433           srcElems.Append( elem );
6434         }
6435         else if ( theCopy ) {
6436           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6437           srcElems.Append( elem );
6438         }
6439         else {
6440           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6441         }
6442       }
6443       break;
6444
6445     case SMDSGeom_BALL: // -------------------- Ball
6446       {
6447         if ( !theCopy && !theTargetMesh ) continue;
6448
6449         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6450         if (nodeMapIt == nodeMap.end())
6451           continue; // not all nodes transformed
6452
6453         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6454         if ( theTargetMesh ) {
6455           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6456           srcElems.Append( elem );
6457         }
6458         else {
6459           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6460           srcElems.Append( elem );
6461         }
6462       }
6463       break;
6464
6465     default: // ----------------------- Regular elements
6466
6467       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6468       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6469       const std::vector<int>& i = needReverse ? iRev : iForw;
6470
6471       // find transformed nodes
6472       vector<const SMDS_MeshNode*> nodes(nbNodes);
6473       int iNode = 0;
6474       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6475       while ( itN->more() ) {
6476         const SMDS_MeshNode* node =
6477           static_cast<const SMDS_MeshNode*>( itN->next() );
6478         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6479         if ( nodeMapIt == nodeMap.end() )
6480           break; // not all nodes transformed
6481         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6482       }
6483       if ( iNode != nbNodes )
6484         continue; // not all nodes transformed
6485
6486       if ( theTargetMesh ) {
6487         if ( SMDS_MeshElement* copy =
6488              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6489           myLastCreatedElems.Append( copy );
6490           srcElems.Append( elem );
6491         }
6492       }
6493       else if ( theCopy ) {
6494         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6495           srcElems.Append( elem );
6496       }
6497       else {
6498         // reverse element as it was reversed by transformation
6499         if ( nbNodes > 2 )
6500           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6501       }
6502     } // switch ( geomType )
6503
6504   } // loop on elements
6505
6506   PGroupIDs newGroupIDs;
6507
6508   if ( ( theMakeGroups && theCopy ) ||
6509        ( theMakeGroups && theTargetMesh ) )
6510     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6511
6512   return newGroupIDs;
6513 }
6514
6515 //=======================================================================
6516 /*!
6517  * \brief Create groups of elements made during transformation
6518  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6519  *  \param elemGens - elements making corresponding myLastCreatedElems
6520  *  \param postfix - to append to names of new groups
6521  *  \param targetMesh - mesh to create groups in
6522  *  \param topPresent - is there "top" elements that are created by sweeping
6523  */
6524 //=======================================================================
6525
6526 SMESH_MeshEditor::PGroupIDs
6527 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6528                                  const SMESH_SequenceOfElemPtr& elemGens,
6529                                  const std::string&             postfix,
6530                                  SMESH_Mesh*                    targetMesh,
6531                                  const bool                     topPresent)
6532 {
6533   PGroupIDs newGroupIDs( new list<int> );
6534   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6535
6536   // Sort existing groups by types and collect their names
6537
6538   // containers to store an old group and generated new ones;
6539   // 1st new group is for result elems of different type than a source one;
6540   // 2nd new group is for same type result elems ("top" group at extrusion)
6541   using boost::tuple;
6542   using boost::make_tuple;
6543   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6544   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6545   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6546   // group names
6547   set< string > groupNames;
6548
6549   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6550   if ( !groupIt->more() ) return newGroupIDs;
6551
6552   int newGroupID = mesh->GetGroupIds().back()+1;
6553   while ( groupIt->more() )
6554   {
6555     SMESH_Group * group = groupIt->next();
6556     if ( !group ) continue;
6557     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6558     if ( !groupDS || groupDS->IsEmpty() ) continue;
6559     groupNames.insert    ( group->GetName() );
6560     groupDS->SetStoreName( group->GetName() );
6561     const SMDSAbs_ElementType type = groupDS->GetType();
6562     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6563     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6564     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6565     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6566   }
6567
6568   // Loop on nodes and elements to add them in new groups
6569
6570   vector< const SMDS_MeshElement* > resultElems;
6571   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6572   {
6573     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6574     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6575     if ( gens.Length() != elems.Length() )
6576       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6577
6578     // loop on created elements
6579     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6580     {
6581       const SMDS_MeshElement* sourceElem = gens( iElem );
6582       if ( !sourceElem ) {
6583         MESSAGE("generateGroups(): NULL source element");
6584         continue;
6585       }
6586       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6587       if ( groupsOldNew.empty() ) { // no groups of this type at all
6588         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6589           ++iElem; // skip all elements made by sourceElem
6590         continue;
6591       }
6592       // collect all elements made by the iElem-th sourceElem
6593       resultElems.clear();
6594       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6595         if ( resElem != sourceElem )
6596           resultElems.push_back( resElem );
6597       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6598         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6599           if ( resElem != sourceElem )
6600             resultElems.push_back( resElem );
6601
6602       const SMDS_MeshElement* topElem = 0;
6603       if ( isNodes ) // there must be a top element
6604       {
6605         topElem = resultElems.back();
6606         resultElems.pop_back();
6607       }
6608       else
6609       {
6610         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6611         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6612           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6613           {
6614             topElem = *resElemIt;
6615             *resElemIt = 0; // erase *resElemIt
6616             break;
6617           }
6618       }
6619       // add resultElems to groups originted from ones the sourceElem belongs to
6620       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6621       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6622       {
6623         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6624         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6625         {
6626           // fill in a new group
6627           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6628           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6629           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6630             if ( *resElemIt )
6631               newGroup.Add( *resElemIt );
6632
6633           // fill a "top" group
6634           if ( topElem )
6635           {
6636             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6637             newTopGroup.Add( topElem );
6638          }
6639         }
6640       }
6641     } // loop on created elements
6642   }// loop on nodes and elements
6643
6644   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6645
6646   list<int> topGrouIds;
6647   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6648   {
6649     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6650     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6651                                       orderedOldNewGroups[i]->get<2>() };
6652     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6653     {
6654       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6655       if ( newGroupDS->IsEmpty() )
6656       {
6657         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6658       }
6659       else
6660       {
6661         // set group type
6662         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6663
6664         // make a name
6665         const bool isTop = ( topPresent &&
6666                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6667                              is2nd );
6668
6669         string name = oldGroupDS->GetStoreName();
6670         { // remove trailing whitespaces (issue 22599)
6671           size_t size = name.size();
6672           while ( size > 1 && isspace( name[ size-1 ]))
6673             --size;
6674           if ( size != name.size() )
6675           {
6676             name.resize( size );
6677             oldGroupDS->SetStoreName( name.c_str() );
6678           }
6679         }
6680         if ( !targetMesh ) {
6681           string suffix = ( isTop ? "top": postfix.c_str() );
6682           name += "_";
6683           name += suffix;
6684           int nb = 1;
6685           while ( !groupNames.insert( name ).second ) // name exists
6686             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6687         }
6688         else if ( isTop ) {
6689           name += "_top";
6690         }
6691         newGroupDS->SetStoreName( name.c_str() );
6692
6693         // make a SMESH_Groups
6694         mesh->AddGroup( newGroupDS );
6695         if ( isTop )
6696           topGrouIds.push_back( newGroupDS->GetID() );
6697         else
6698           newGroupIDs->push_back( newGroupDS->GetID() );
6699       }
6700     }
6701   }
6702   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6703
6704   return newGroupIDs;
6705 }
6706
6707 //================================================================================
6708 /*!
6709  * \brief Return list of group of nodes close to each other within theTolerance
6710  *        Search among theNodes or in the whole mesh if theNodes is empty using
6711  *        an Octree algorithm
6712  */
6713 //================================================================================
6714
6715 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6716                                             const double         theTolerance,
6717                                             TListOfListOfNodes & theGroupsOfNodes)
6718 {
6719   myLastCreatedElems.Clear();
6720   myLastCreatedNodes.Clear();
6721
6722   if ( theNodes.empty() )
6723   { // get all nodes in the mesh
6724     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6725     while ( nIt->more() )
6726       theNodes.insert( theNodes.end(),nIt->next());
6727   }
6728
6729   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6730 }
6731
6732 //=======================================================================
6733 //function : SimplifyFace
6734 //purpose  :
6735 //=======================================================================
6736
6737 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6738                                     vector<const SMDS_MeshNode *>&       poly_nodes,
6739                                     vector<int>&                         quantities) const
6740 {
6741   int nbNodes = faceNodes.size();
6742
6743   if (nbNodes < 3)
6744     return 0;
6745
6746   set<const SMDS_MeshNode*> nodeSet;
6747
6748   // get simple seq of nodes
6749   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6750   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6751   int iSimple = 0, nbUnique = 0;
6752
6753   simpleNodes[iSimple++] = faceNodes[0];
6754   nbUnique++;
6755   for (int iCur = 1; iCur < nbNodes; iCur++) {
6756     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6757       simpleNodes[iSimple++] = faceNodes[iCur];
6758       if (nodeSet.insert( faceNodes[iCur] ).second)
6759         nbUnique++;
6760     }
6761   }
6762   int nbSimple = iSimple;
6763   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6764     nbSimple--;
6765     iSimple--;
6766   }
6767
6768   if (nbUnique < 3)
6769     return 0;
6770
6771   // separate loops
6772   int nbNew = 0;
6773   bool foundLoop = (nbSimple > nbUnique);
6774   while (foundLoop) {
6775     foundLoop = false;
6776     set<const SMDS_MeshNode*> loopSet;
6777     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6778       const SMDS_MeshNode* n = simpleNodes[iSimple];
6779       if (!loopSet.insert( n ).second) {
6780         foundLoop = true;
6781
6782         // separate loop
6783         int iC = 0, curLast = iSimple;
6784         for (; iC < curLast; iC++) {
6785           if (simpleNodes[iC] == n) break;
6786         }
6787         int loopLen = curLast - iC;
6788         if (loopLen > 2) {
6789           // create sub-element
6790           nbNew++;
6791           quantities.push_back(loopLen);
6792           for (; iC < curLast; iC++) {
6793             poly_nodes.push_back(simpleNodes[iC]);
6794           }
6795         }
6796         // shift the rest nodes (place from the first loop position)
6797         for (iC = curLast + 1; iC < nbSimple; iC++) {
6798           simpleNodes[iC - loopLen] = simpleNodes[iC];
6799         }
6800         nbSimple -= loopLen;
6801         iSimple -= loopLen;
6802       }
6803     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6804   } // while (foundLoop)
6805
6806   if (iSimple > 2) {
6807     nbNew++;
6808     quantities.push_back(iSimple);
6809     for (int i = 0; i < iSimple; i++)
6810       poly_nodes.push_back(simpleNodes[i]);
6811   }
6812
6813   return nbNew;
6814 }
6815
6816 //=======================================================================
6817 //function : MergeNodes
6818 //purpose  : In each group, the cdr of nodes are substituted by the first one
6819 //           in all elements.
6820 //=======================================================================
6821
6822 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6823 {
6824   MESSAGE("MergeNodes");
6825   myLastCreatedElems.Clear();
6826   myLastCreatedNodes.Clear();
6827
6828   SMESHDS_Mesh* aMesh = GetMeshDS();
6829
6830   TNodeNodeMap nodeNodeMap; // node to replace - new node
6831   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6832   list< int > rmElemIds, rmNodeIds;
6833
6834   // Fill nodeNodeMap and elems
6835
6836   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6837   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6838     list<const SMDS_MeshNode*>& nodes = *grIt;
6839     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6840     const SMDS_MeshNode* nToKeep = *nIt;
6841     //MESSAGE("node to keep " << nToKeep->GetID());
6842     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6843       const SMDS_MeshNode* nToRemove = *nIt;
6844       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6845       if ( nToRemove != nToKeep ) {
6846         //MESSAGE("  node to remove " << nToRemove->GetID());
6847         rmNodeIds.push_back( nToRemove->GetID() );
6848         AddToSameGroups( nToKeep, nToRemove, aMesh );
6849         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6850         // after MergeNodes() w/o creating node in place of merged ones.
6851         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6852         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6853           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6854             sm->SetIsAlwaysComputed( true );
6855       }
6856
6857       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6858       while ( invElemIt->more() ) {
6859         const SMDS_MeshElement* elem = invElemIt->next();
6860         elems.insert(elem);
6861       }
6862     }
6863   }
6864   // Change element nodes or remove an element
6865
6866   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6867   for ( ; eIt != elems.end(); eIt++ ) {
6868     const SMDS_MeshElement* elem = *eIt;
6869     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6870     int nbNodes = elem->NbNodes();
6871     int aShapeId = FindShape( elem );
6872
6873     set<const SMDS_MeshNode*> nodeSet;
6874     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6875     int iUnique = 0, iCur = 0, nbRepl = 0;
6876     vector<int> iRepl( nbNodes );
6877
6878     // get new seq of nodes
6879     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6880     while ( itN->more() ) {
6881       const SMDS_MeshNode* n =
6882         static_cast<const SMDS_MeshNode*>( itN->next() );
6883
6884       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6885       if ( nnIt != nodeNodeMap.end() ) { // n sticks
6886         n = (*nnIt).second;
6887         // BUG 0020185: begin
6888         {
6889           bool stopRecur = false;
6890           set<const SMDS_MeshNode*> nodesRecur;
6891           nodesRecur.insert(n);
6892           while (!stopRecur) {
6893             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6894             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6895               n = (*nnIt_i).second;
6896               if (!nodesRecur.insert(n).second) {
6897                 // error: recursive dependancy
6898                 stopRecur = true;
6899               }
6900             }
6901             else
6902               stopRecur = true;
6903           }
6904         }
6905         // BUG 0020185: end
6906       }
6907       curNodes[ iCur ] = n;
6908       bool isUnique = nodeSet.insert( n ).second;
6909       if ( isUnique )
6910         uniqueNodes[ iUnique++ ] = n;
6911       else
6912         iRepl[ nbRepl++ ] = iCur;
6913       iCur++;
6914     }
6915
6916     // Analyse element topology after replacement
6917
6918     bool isOk = true;
6919     int nbUniqueNodes = nodeSet.size();
6920     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6921     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6922       // Polygons and Polyhedral volumes
6923       if (elem->IsPoly()) {
6924
6925         if (elem->GetType() == SMDSAbs_Face) {
6926           // Polygon
6927           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6928           int inode = 0;
6929           for (; inode < nbNodes; inode++) {
6930             face_nodes[inode] = curNodes[inode];
6931           }
6932
6933           vector<const SMDS_MeshNode *> polygons_nodes;
6934           vector<int> quantities;
6935           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6936           if (nbNew > 0) {
6937             inode = 0;
6938             for (int iface = 0; iface < nbNew; iface++) {
6939               int nbNodes = quantities[iface];
6940               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6941               for (int ii = 0; ii < nbNodes; ii++, inode++) {
6942                 poly_nodes[ii] = polygons_nodes[inode];
6943               }
6944               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6945               myLastCreatedElems.Append(newElem);
6946               if (aShapeId)
6947                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6948             }
6949
6950             MESSAGE("ChangeElementNodes MergeNodes Polygon");
6951             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6952             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6953             int quid =0;
6954             if (nbNew > 0) quid = nbNew - 1;
6955             vector<int> newquant(quantities.begin()+quid, quantities.end());
6956             const SMDS_MeshElement* newElem = 0;
6957             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6958             myLastCreatedElems.Append(newElem);
6959             if ( aShapeId && newElem )
6960               aMesh->SetMeshElementOnShape( newElem, aShapeId );
6961             rmElemIds.push_back(elem->GetID());
6962           }
6963           else {
6964             rmElemIds.push_back(elem->GetID());
6965           }
6966
6967         }
6968         else if (elem->GetType() == SMDSAbs_Volume) {
6969           // Polyhedral volume
6970           if (nbUniqueNodes < 4) {
6971             rmElemIds.push_back(elem->GetID());
6972           }
6973           else {
6974             // each face has to be analyzed in order to check volume validity
6975             const SMDS_VtkVolume* aPolyedre =
6976               dynamic_cast<const SMDS_VtkVolume*>( elem );
6977             if (aPolyedre) {
6978               int nbFaces = aPolyedre->NbFaces();
6979
6980               vector<const SMDS_MeshNode *> poly_nodes;
6981               vector<int> quantities;
6982
6983               for (int iface = 1; iface <= nbFaces; iface++) {
6984                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6985                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6986
6987                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6988                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6989                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6990                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6991                     faceNode = (*nnIt).second;
6992                   }
6993                   faceNodes[inode - 1] = faceNode;
6994                 }
6995
6996                 SimplifyFace(faceNodes, poly_nodes, quantities);
6997               }
6998
6999               if (quantities.size() > 3) {
7000                 // to be done: remove coincident faces
7001               }
7002
7003               if (quantities.size() > 3)
7004                 {
7005                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7006                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7007                   const SMDS_MeshElement* newElem = 0;
7008                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7009                   myLastCreatedElems.Append(newElem);
7010                   if ( aShapeId && newElem )
7011                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7012                   rmElemIds.push_back(elem->GetID());
7013                 }
7014             }
7015             else {
7016               rmElemIds.push_back(elem->GetID());
7017             }
7018           }
7019         }
7020         else {
7021         }
7022
7023         continue;
7024       } // poly element
7025
7026       // Regular elements
7027       // TODO not all the possible cases are solved. Find something more generic?
7028       switch ( nbNodes ) {
7029       case 2: ///////////////////////////////////// EDGE
7030         isOk = false; break;
7031       case 3: ///////////////////////////////////// TRIANGLE
7032         isOk = false; break;
7033       case 4:
7034         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7035           isOk = false;
7036         else { //////////////////////////////////// QUADRANGLE
7037           if ( nbUniqueNodes < 3 )
7038             isOk = false;
7039           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7040             isOk = false; // opposite nodes stick
7041           //MESSAGE("isOk " << isOk);
7042         }
7043         break;
7044       case 6: ///////////////////////////////////// PENTAHEDRON
7045         if ( nbUniqueNodes == 4 ) {
7046           // ---------------------------------> tetrahedron
7047           if (nbRepl == 3 &&
7048               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7049             // all top nodes stick: reverse a bottom
7050             uniqueNodes[ 0 ] = curNodes [ 1 ];
7051             uniqueNodes[ 1 ] = curNodes [ 0 ];
7052           }
7053           else if (nbRepl == 3 &&
7054                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7055             // all bottom nodes stick: set a top before
7056             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7057             uniqueNodes[ 0 ] = curNodes [ 3 ];
7058             uniqueNodes[ 1 ] = curNodes [ 4 ];
7059             uniqueNodes[ 2 ] = curNodes [ 5 ];
7060           }
7061           else if (nbRepl == 4 &&
7062                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7063             // a lateral face turns into a line: reverse a bottom
7064             uniqueNodes[ 0 ] = curNodes [ 1 ];
7065             uniqueNodes[ 1 ] = curNodes [ 0 ];
7066           }
7067           else
7068             isOk = false;
7069         }
7070         else if ( nbUniqueNodes == 5 ) {
7071           // PENTAHEDRON --------------------> 2 tetrahedrons
7072           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7073             // a bottom node sticks with a linked top one
7074             // 1.
7075             SMDS_MeshElement* newElem =
7076               aMesh->AddVolume(curNodes[ 3 ],
7077                                curNodes[ 4 ],
7078                                curNodes[ 5 ],
7079                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7080             myLastCreatedElems.Append(newElem);
7081             if ( aShapeId )
7082               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7083             // 2. : reverse a bottom
7084             uniqueNodes[ 0 ] = curNodes [ 1 ];
7085             uniqueNodes[ 1 ] = curNodes [ 0 ];
7086             nbUniqueNodes = 4;
7087           }
7088           else
7089             isOk = false;
7090         }
7091         else
7092           isOk = false;
7093         break;
7094       case 8: {
7095         if(elem->IsQuadratic()) { // Quadratic quadrangle
7096           //   1    5    2
7097           //    +---+---+
7098           //    |       |
7099           //    |       |
7100           //   4+       +6
7101           //    |       |
7102           //    |       |
7103           //    +---+---+
7104           //   0    7    3
7105           isOk = false;
7106           if(nbRepl==2) {
7107             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7108           }
7109           if(nbRepl==3) {
7110             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7111             nbUniqueNodes = 6;
7112             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7113               uniqueNodes[0] = curNodes[0];
7114               uniqueNodes[1] = curNodes[2];
7115               uniqueNodes[2] = curNodes[3];
7116               uniqueNodes[3] = curNodes[5];
7117               uniqueNodes[4] = curNodes[6];
7118               uniqueNodes[5] = curNodes[7];
7119               isOk = true;
7120             }
7121             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7122               uniqueNodes[0] = curNodes[0];
7123               uniqueNodes[1] = curNodes[1];
7124               uniqueNodes[2] = curNodes[2];
7125               uniqueNodes[3] = curNodes[4];
7126               uniqueNodes[4] = curNodes[5];
7127               uniqueNodes[5] = curNodes[6];
7128               isOk = true;
7129             }
7130             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7131               uniqueNodes[0] = curNodes[1];
7132               uniqueNodes[1] = curNodes[2];
7133               uniqueNodes[2] = curNodes[3];
7134               uniqueNodes[3] = curNodes[5];
7135               uniqueNodes[4] = curNodes[6];
7136               uniqueNodes[5] = curNodes[0];
7137               isOk = true;
7138             }
7139             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7140               uniqueNodes[0] = curNodes[0];
7141               uniqueNodes[1] = curNodes[1];
7142               uniqueNodes[2] = curNodes[3];
7143               uniqueNodes[3] = curNodes[4];
7144               uniqueNodes[4] = curNodes[6];
7145               uniqueNodes[5] = curNodes[7];
7146               isOk = true;
7147             }
7148             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7149               uniqueNodes[0] = curNodes[0];
7150               uniqueNodes[1] = curNodes[2];
7151               uniqueNodes[2] = curNodes[3];
7152               uniqueNodes[3] = curNodes[1];
7153               uniqueNodes[4] = curNodes[6];
7154               uniqueNodes[5] = curNodes[7];
7155               isOk = true;
7156             }
7157             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7158               uniqueNodes[0] = curNodes[0];
7159               uniqueNodes[1] = curNodes[1];
7160               uniqueNodes[2] = curNodes[2];
7161               uniqueNodes[3] = curNodes[4];
7162               uniqueNodes[4] = curNodes[5];
7163               uniqueNodes[5] = curNodes[7];
7164               isOk = true;
7165             }
7166             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7167               uniqueNodes[0] = curNodes[0];
7168               uniqueNodes[1] = curNodes[1];
7169               uniqueNodes[2] = curNodes[3];
7170               uniqueNodes[3] = curNodes[4];
7171               uniqueNodes[4] = curNodes[2];
7172               uniqueNodes[5] = curNodes[7];
7173               isOk = true;
7174             }
7175             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7176               uniqueNodes[0] = curNodes[0];
7177               uniqueNodes[1] = curNodes[1];
7178               uniqueNodes[2] = curNodes[2];
7179               uniqueNodes[3] = curNodes[4];
7180               uniqueNodes[4] = curNodes[5];
7181               uniqueNodes[5] = curNodes[3];
7182               isOk = true;
7183             }
7184           }
7185           if(nbRepl==4) {
7186             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7187           }
7188           if(nbRepl==5) {
7189             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7190           }
7191           break;
7192         }
7193         //////////////////////////////////// HEXAHEDRON
7194         isOk = false;
7195         SMDS_VolumeTool hexa (elem);
7196         hexa.SetExternalNormal();
7197         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7198           //////////////////////// HEX ---> 1 tetrahedron
7199           for ( int iFace = 0; iFace < 6; iFace++ ) {
7200             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7201             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7202                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7203                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7204               // one face turns into a point ...
7205               int iOppFace = hexa.GetOppFaceIndex( iFace );
7206               ind = hexa.GetFaceNodesIndices( iOppFace );
7207               int nbStick = 0;
7208               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7209                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7210                   nbStick++;
7211               }
7212               if ( nbStick == 1 ) {
7213                 // ... and the opposite one - into a triangle.
7214                 // set a top node
7215                 ind = hexa.GetFaceNodesIndices( iFace );
7216                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7217                 isOk = true;
7218               }
7219               break;
7220             }
7221           }
7222         }
7223         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7224           //////////////////////// HEX ---> 1 prism
7225           int nbTria = 0, iTria[3];
7226           const int *ind; // indices of face nodes
7227           // look for triangular faces
7228           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7229             ind = hexa.GetFaceNodesIndices( iFace );
7230             TIDSortedNodeSet faceNodes;
7231             for ( iCur = 0; iCur < 4; iCur++ )
7232               faceNodes.insert( curNodes[ind[iCur]] );
7233             if ( faceNodes.size() == 3 )
7234               iTria[ nbTria++ ] = iFace;
7235           }
7236           // check if triangles are opposite
7237           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7238           {
7239             isOk = true;
7240             // set nodes of the bottom triangle
7241             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7242             vector<int> indB;
7243             for ( iCur = 0; iCur < 4; iCur++ )
7244               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7245                 indB.push_back( ind[iCur] );
7246             if ( !hexa.IsForward() )
7247               std::swap( indB[0], indB[2] );
7248             for ( iCur = 0; iCur < 3; iCur++ )
7249               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7250             // set nodes of the top triangle
7251             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7252             for ( iCur = 0; iCur < 3; ++iCur )
7253               for ( int j = 0; j < 4; ++j )
7254                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7255                 {
7256                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7257                   break;
7258                 }
7259           }
7260           break;
7261         }
7262         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7263           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7264           for ( int iFace = 0; iFace < 6; iFace++ ) {
7265             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7266             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7267                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7268                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7269               // one face turns into a point ...
7270               int iOppFace = hexa.GetOppFaceIndex( iFace );
7271               ind = hexa.GetFaceNodesIndices( iOppFace );
7272               int nbStick = 0;
7273               iUnique = 2;  // reverse a tetrahedron 1 bottom
7274               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7275                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7276                   nbStick++;
7277                 else if ( iUnique >= 0 )
7278                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7279               }
7280               if ( nbStick == 0 ) {
7281                 // ... and the opposite one is a quadrangle
7282                 // set a top node
7283                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7284                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7285                 nbUniqueNodes = 4;
7286                 // tetrahedron 2
7287                 SMDS_MeshElement* newElem =
7288                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7289                                    curNodes[ind[ 3 ]],
7290                                    curNodes[ind[ 2 ]],
7291                                    curNodes[indTop[ 0 ]]);
7292                 myLastCreatedElems.Append(newElem);
7293                 if ( aShapeId )
7294                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7295                 isOk = true;
7296               }
7297               break;
7298             }
7299           }
7300         }
7301         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7302           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7303           // find indices of quad and tri faces
7304           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7305           for ( iFace = 0; iFace < 6; iFace++ ) {
7306             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7307             nodeSet.clear();
7308             for ( iCur = 0; iCur < 4; iCur++ )
7309               nodeSet.insert( curNodes[ind[ iCur ]] );
7310             nbUniqueNodes = nodeSet.size();
7311             if ( nbUniqueNodes == 3 )
7312               iTriFace[ nbTri++ ] = iFace;
7313             else if ( nbUniqueNodes == 4 )
7314               iQuadFace[ nbQuad++ ] = iFace;
7315           }
7316           if (nbQuad == 2 && nbTri == 4 &&
7317               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7318             // 2 opposite quadrangles stuck with a diagonal;
7319             // sample groups of merged indices: (0-4)(2-6)
7320             // --------------------------------------------> 2 tetrahedrons
7321             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7322             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7323             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7324             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7325                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7326               // stuck with 0-2 diagonal
7327               i0  = ind1[ 3 ];
7328               i1d = ind1[ 0 ];
7329               i2  = ind1[ 1 ];
7330               i3d = ind1[ 2 ];
7331               i0t = ind2[ 1 ];
7332               i2t = ind2[ 3 ];
7333             }
7334             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7335                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7336               // stuck with 1-3 diagonal
7337               i0  = ind1[ 0 ];
7338               i1d = ind1[ 1 ];
7339               i2  = ind1[ 2 ];
7340               i3d = ind1[ 3 ];
7341               i0t = ind2[ 0 ];
7342               i2t = ind2[ 1 ];
7343             }
7344             else {
7345               ASSERT(0);
7346             }
7347             // tetrahedron 1
7348             uniqueNodes[ 0 ] = curNodes [ i0 ];
7349             uniqueNodes[ 1 ] = curNodes [ i1d ];
7350             uniqueNodes[ 2 ] = curNodes [ i3d ];
7351             uniqueNodes[ 3 ] = curNodes [ i0t ];
7352             nbUniqueNodes = 4;
7353             // tetrahedron 2
7354             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7355                                                          curNodes[ i2 ],
7356                                                          curNodes[ i3d ],
7357                                                          curNodes[ i2t ]);
7358             myLastCreatedElems.Append(newElem);
7359             if ( aShapeId )
7360               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7361             isOk = true;
7362           }
7363           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7364                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7365             // --------------------------------------------> prism
7366             // find 2 opposite triangles
7367             nbUniqueNodes = 6;
7368             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7369               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7370                 // find indices of kept and replaced nodes
7371                 // and fill unique nodes of 2 opposite triangles
7372                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7373                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7374                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7375                 // fill unique nodes
7376                 iUnique = 0;
7377                 isOk = true;
7378                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7379                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7380                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7381                   if ( n == nInit ) {
7382                     // iCur of a linked node of the opposite face (make normals co-directed):
7383                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7384                     // check that correspondent corners of triangles are linked
7385                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7386                       isOk = false;
7387                     else {
7388                       uniqueNodes[ iUnique ] = n;
7389                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7390                       iUnique++;
7391                     }
7392                   }
7393                 }
7394                 break;
7395               }
7396             }
7397           }
7398         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7399         else
7400         {
7401           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7402         }
7403         break;
7404       } // HEXAHEDRON
7405
7406       default:
7407         isOk = false;
7408       } // switch ( nbNodes )
7409
7410     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7411
7412     if ( isOk ) { // the elem remains valid after sticking nodes
7413       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7414       {
7415         // Change nodes of polyedre
7416         const SMDS_VtkVolume* aPolyedre =
7417           dynamic_cast<const SMDS_VtkVolume*>( elem );
7418         if (aPolyedre) {
7419           int nbFaces = aPolyedre->NbFaces();
7420
7421           vector<const SMDS_MeshNode *> poly_nodes;
7422           vector<int> quantities (nbFaces);
7423
7424           for (int iface = 1; iface <= nbFaces; iface++) {
7425             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7426             quantities[iface - 1] = nbFaceNodes;
7427
7428             for (inode = 1; inode <= nbFaceNodes; inode++) {
7429               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7430
7431               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7432               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7433                 curNode = (*nnIt).second;
7434               }
7435               poly_nodes.push_back(curNode);
7436             }
7437           }
7438           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7439         }
7440       }
7441       else // replace non-polyhedron elements
7442       {
7443         const SMDSAbs_ElementType etyp = elem->GetType();
7444         const int elemId               = elem->GetID();
7445         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7446         uniqueNodes.resize(nbUniqueNodes);
7447
7448         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7449
7450         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7451         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7452         if ( sm && newElem )
7453           sm->AddElement( newElem );
7454         if ( elem != newElem )
7455           ReplaceElemInGroups( elem, newElem, aMesh );
7456       }
7457     }
7458     else {
7459       // Remove invalid regular element or invalid polygon
7460       rmElemIds.push_back( elem->GetID() );
7461     }
7462
7463   } // loop on elements
7464
7465   // Remove bad elements, then equal nodes (order important)
7466
7467   Remove( rmElemIds, false );
7468   Remove( rmNodeIds, true );
7469
7470 }
7471
7472
7473 // ========================================================
7474 // class   : SortableElement
7475 // purpose : allow sorting elements basing on their nodes
7476 // ========================================================
7477 class SortableElement : public set <const SMDS_MeshElement*>
7478 {
7479 public:
7480
7481   SortableElement( const SMDS_MeshElement* theElem )
7482   {
7483     myElem = theElem;
7484     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7485     while ( nodeIt->more() )
7486       this->insert( nodeIt->next() );
7487   }
7488
7489   const SMDS_MeshElement* Get() const
7490   { return myElem; }
7491
7492   void Set(const SMDS_MeshElement* e) const
7493   { myElem = e; }
7494
7495
7496 private:
7497   mutable const SMDS_MeshElement* myElem;
7498 };
7499
7500 //=======================================================================
7501 //function : FindEqualElements
7502 //purpose  : Return list of group of elements built on the same nodes.
7503 //           Search among theElements or in the whole mesh if theElements is empty
7504 //=======================================================================
7505
7506 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7507                                          TListOfListOfElementsID & theGroupsOfElementsID)
7508 {
7509   myLastCreatedElems.Clear();
7510   myLastCreatedNodes.Clear();
7511
7512   typedef map< SortableElement, int > TMapOfNodeSet;
7513   typedef list<int> TGroupOfElems;
7514
7515   if ( theElements.empty() )
7516   { // get all elements in the mesh
7517     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7518     while ( eIt->more() )
7519       theElements.insert( theElements.end(), eIt->next());
7520   }
7521
7522   vector< TGroupOfElems > arrayOfGroups;
7523   TGroupOfElems groupOfElems;
7524   TMapOfNodeSet mapOfNodeSet;
7525
7526   TIDSortedElemSet::iterator elemIt = theElements.begin();
7527   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7528     const SMDS_MeshElement* curElem = *elemIt;
7529     SortableElement SE(curElem);
7530     int ind = -1;
7531     // check uniqueness
7532     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7533     if( !(pp.second) ) {
7534       TMapOfNodeSet::iterator& itSE = pp.first;
7535       ind = (*itSE).second;
7536       arrayOfGroups[ind].push_back(curElem->GetID());
7537     }
7538     else {
7539       groupOfElems.clear();
7540       groupOfElems.push_back(curElem->GetID());
7541       arrayOfGroups.push_back(groupOfElems);
7542       i++;
7543     }
7544   }
7545
7546   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7547   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7548     groupOfElems = *groupIt;
7549     if ( groupOfElems.size() > 1 ) {
7550       groupOfElems.sort();
7551       theGroupsOfElementsID.push_back(groupOfElems);
7552     }
7553   }
7554 }
7555
7556 //=======================================================================
7557 //function : MergeElements
7558 //purpose  : In each given group, substitute all elements by the first one.
7559 //=======================================================================
7560
7561 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7562 {
7563   myLastCreatedElems.Clear();
7564   myLastCreatedNodes.Clear();
7565
7566   typedef list<int> TListOfIDs;
7567   TListOfIDs rmElemIds; // IDs of elems to remove
7568
7569   SMESHDS_Mesh* aMesh = GetMeshDS();
7570
7571   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7572   while ( groupsIt != theGroupsOfElementsID.end() ) {
7573     TListOfIDs& aGroupOfElemID = *groupsIt;
7574     aGroupOfElemID.sort();
7575     int elemIDToKeep = aGroupOfElemID.front();
7576     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7577     aGroupOfElemID.pop_front();
7578     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7579     while ( idIt != aGroupOfElemID.end() ) {
7580       int elemIDToRemove = *idIt;
7581       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7582       // add the kept element in groups of removed one (PAL15188)
7583       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7584       rmElemIds.push_back( elemIDToRemove );
7585       ++idIt;
7586     }
7587     ++groupsIt;
7588   }
7589
7590   Remove( rmElemIds, false );
7591 }
7592
7593 //=======================================================================
7594 //function : MergeEqualElements
7595 //purpose  : Remove all but one of elements built on the same nodes.
7596 //=======================================================================
7597
7598 void SMESH_MeshEditor::MergeEqualElements()
7599 {
7600   TIDSortedElemSet aMeshElements; /* empty input ==
7601                                      to merge equal elements in the whole mesh */
7602   TListOfListOfElementsID aGroupsOfElementsID;
7603   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7604   MergeElements(aGroupsOfElementsID);
7605 }
7606
7607 //=======================================================================
7608 //function : findAdjacentFace
7609 //purpose  :
7610 //=======================================================================
7611
7612 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7613                                                 const SMDS_MeshNode* n2,
7614                                                 const SMDS_MeshElement* elem)
7615 {
7616   TIDSortedElemSet elemSet, avoidSet;
7617   if ( elem )
7618     avoidSet.insert ( elem );
7619   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7620 }
7621
7622 //=======================================================================
7623 //function : FindFreeBorder
7624 //purpose  :
7625 //=======================================================================
7626
7627 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7628
7629 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7630                                        const SMDS_MeshNode*             theSecondNode,
7631                                        const SMDS_MeshNode*             theLastNode,
7632                                        list< const SMDS_MeshNode* > &   theNodes,
7633                                        list< const SMDS_MeshElement* >& theFaces)
7634 {
7635   if ( !theFirstNode || !theSecondNode )
7636     return false;
7637   // find border face between theFirstNode and theSecondNode
7638   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7639   if ( !curElem )
7640     return false;
7641
7642   theFaces.push_back( curElem );
7643   theNodes.push_back( theFirstNode );
7644   theNodes.push_back( theSecondNode );
7645
7646   //vector<const SMDS_MeshNode*> nodes;
7647   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7648   TIDSortedElemSet foundElems;
7649   bool needTheLast = ( theLastNode != 0 );
7650
7651   while ( nStart != theLastNode ) {
7652     if ( nStart == theFirstNode )
7653       return !needTheLast;
7654
7655     // find all free border faces sharing form nStart
7656
7657     list< const SMDS_MeshElement* > curElemList;
7658     list< const SMDS_MeshNode* > nStartList;
7659     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7660     while ( invElemIt->more() ) {
7661       const SMDS_MeshElement* e = invElemIt->next();
7662       if ( e == curElem || foundElems.insert( e ).second ) {
7663         // get nodes
7664         int iNode = 0, nbNodes = e->NbNodes();
7665         //const SMDS_MeshNode* nodes[nbNodes+1];
7666         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7667
7668         if(e->IsQuadratic()) {
7669           const SMDS_VtkFace* F =
7670             dynamic_cast<const SMDS_VtkFace*>(e);
7671           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7672           // use special nodes iterator
7673           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7674           while( anIter->more() ) {
7675             nodes[ iNode++ ] = cast2Node(anIter->next());
7676           }
7677         }
7678         else {
7679           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7680           while ( nIt->more() )
7681             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7682         }
7683         nodes[ iNode ] = nodes[ 0 ];
7684         // check 2 links
7685         for ( iNode = 0; iNode < nbNodes; iNode++ )
7686           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7687                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7688               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7689           {
7690             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7691             curElemList.push_back( e );
7692           }
7693       }
7694     }
7695     // analyse the found
7696
7697     int nbNewBorders = curElemList.size();
7698     if ( nbNewBorders == 0 ) {
7699       // no free border furthermore
7700       return !needTheLast;
7701     }
7702     else if ( nbNewBorders == 1 ) {
7703       // one more element found
7704       nIgnore = nStart;
7705       nStart = nStartList.front();
7706       curElem = curElemList.front();
7707       theFaces.push_back( curElem );
7708       theNodes.push_back( nStart );
7709     }
7710     else {
7711       // several continuations found
7712       list< const SMDS_MeshElement* >::iterator curElemIt;
7713       list< const SMDS_MeshNode* >::iterator nStartIt;
7714       // check if one of them reached the last node
7715       if ( needTheLast ) {
7716         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7717              curElemIt!= curElemList.end();
7718              curElemIt++, nStartIt++ )
7719           if ( *nStartIt == theLastNode ) {
7720             theFaces.push_back( *curElemIt );
7721             theNodes.push_back( *nStartIt );
7722             return true;
7723           }
7724       }
7725       // find the best free border by the continuations
7726       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7727       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7728       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7729            curElemIt!= curElemList.end();
7730            curElemIt++, nStartIt++ )
7731       {
7732         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7733         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7734         // find one more free border
7735         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7736           cNL->clear();
7737           cFL->clear();
7738         }
7739         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7740           // choice: clear a worse one
7741           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7742           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7743           contNodes[ iWorse ].clear();
7744           contFaces[ iWorse ].clear();
7745         }
7746       }
7747       if ( contNodes[0].empty() && contNodes[1].empty() )
7748         return false;
7749
7750       // append the best free border
7751       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7752       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7753       theNodes.pop_back(); // remove nIgnore
7754       theNodes.pop_back(); // remove nStart
7755       theFaces.pop_back(); // remove curElem
7756       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7757       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7758       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7759       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7760       return true;
7761
7762     } // several continuations found
7763   } // while ( nStart != theLastNode )
7764
7765   return true;
7766 }
7767
7768 //=======================================================================
7769 //function : CheckFreeBorderNodes
7770 //purpose  : Return true if the tree nodes are on a free border
7771 //=======================================================================
7772
7773 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7774                                             const SMDS_MeshNode* theNode2,
7775                                             const SMDS_MeshNode* theNode3)
7776 {
7777   list< const SMDS_MeshNode* > nodes;
7778   list< const SMDS_MeshElement* > faces;
7779   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7780 }
7781
7782 //=======================================================================
7783 //function : SewFreeBorder
7784 //purpose  :
7785 //=======================================================================
7786
7787 SMESH_MeshEditor::Sew_Error
7788 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7789                                  const SMDS_MeshNode* theBordSecondNode,
7790                                  const SMDS_MeshNode* theBordLastNode,
7791                                  const SMDS_MeshNode* theSideFirstNode,
7792                                  const SMDS_MeshNode* theSideSecondNode,
7793                                  const SMDS_MeshNode* theSideThirdNode,
7794                                  const bool           theSideIsFreeBorder,
7795                                  const bool           toCreatePolygons,
7796                                  const bool           toCreatePolyedrs)
7797 {
7798   myLastCreatedElems.Clear();
7799   myLastCreatedNodes.Clear();
7800
7801   MESSAGE("::SewFreeBorder()");
7802   Sew_Error aResult = SEW_OK;
7803
7804   // ====================================
7805   //    find side nodes and elements
7806   // ====================================
7807
7808   list< const SMDS_MeshNode* > nSide[ 2 ];
7809   list< const SMDS_MeshElement* > eSide[ 2 ];
7810   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7811   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7812
7813   // Free border 1
7814   // --------------
7815   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7816                       nSide[0], eSide[0])) {
7817     MESSAGE(" Free Border 1 not found " );
7818     aResult = SEW_BORDER1_NOT_FOUND;
7819   }
7820   if (theSideIsFreeBorder) {
7821     // Free border 2
7822     // --------------
7823     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7824                         nSide[1], eSide[1])) {
7825       MESSAGE(" Free Border 2 not found " );
7826       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7827     }
7828   }
7829   if ( aResult != SEW_OK )
7830     return aResult;
7831
7832   if (!theSideIsFreeBorder) {
7833     // Side 2
7834     // --------------
7835
7836     // -------------------------------------------------------------------------
7837     // Algo:
7838     // 1. If nodes to merge are not coincident, move nodes of the free border
7839     //    from the coord sys defined by the direction from the first to last
7840     //    nodes of the border to the correspondent sys of the side 2
7841     // 2. On the side 2, find the links most co-directed with the correspondent
7842     //    links of the free border
7843     // -------------------------------------------------------------------------
7844
7845     // 1. Since sewing may break if there are volumes to split on the side 2,
7846     //    we wont move nodes but just compute new coordinates for them
7847     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7848     TNodeXYZMap nBordXYZ;
7849     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7850     list< const SMDS_MeshNode* >::iterator nBordIt;
7851
7852     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7853     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7854     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7855     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7856     double tol2 = 1.e-8;
7857     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7858     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7859       // Need node movement.
7860
7861       // find X and Z axes to create trsf
7862       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7863       gp_Vec X = Zs ^ Zb;
7864       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7865         // Zb || Zs
7866         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7867
7868       // coord systems
7869       gp_Ax3 toBordAx( Pb1, Zb, X );
7870       gp_Ax3 fromSideAx( Ps1, Zs, X );
7871       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7872       // set trsf
7873       gp_Trsf toBordSys, fromSide2Sys;
7874       toBordSys.SetTransformation( toBordAx );
7875       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7876       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7877
7878       // move
7879       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7880         const SMDS_MeshNode* n = *nBordIt;
7881         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7882         toBordSys.Transforms( xyz );
7883         fromSide2Sys.Transforms( xyz );
7884         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7885       }
7886     }
7887     else {
7888       // just insert nodes XYZ in the nBordXYZ map
7889       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7890         const SMDS_MeshNode* n = *nBordIt;
7891         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7892       }
7893     }
7894
7895     // 2. On the side 2, find the links most co-directed with the correspondent
7896     //    links of the free border
7897
7898     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7899     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7900     sideNodes.push_back( theSideFirstNode );
7901
7902     bool hasVolumes = false;
7903     LinkID_Gen aLinkID_Gen( GetMeshDS() );
7904     set<long> foundSideLinkIDs, checkedLinkIDs;
7905     SMDS_VolumeTool volume;
7906     //const SMDS_MeshNode* faceNodes[ 4 ];
7907
7908     const SMDS_MeshNode*    sideNode;
7909     const SMDS_MeshElement* sideElem;
7910     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7911     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7912     nBordIt = bordNodes.begin();
7913     nBordIt++;
7914     // border node position and border link direction to compare with
7915     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7916     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7917     // choose next side node by link direction or by closeness to
7918     // the current border node:
7919     bool searchByDir = ( *nBordIt != theBordLastNode );
7920     do {
7921       // find the next node on the Side 2
7922       sideNode = 0;
7923       double maxDot = -DBL_MAX, minDist = DBL_MAX;
7924       long linkID;
7925       checkedLinkIDs.clear();
7926       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7927
7928       // loop on inverse elements of current node (prevSideNode) on the Side 2
7929       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7930       while ( invElemIt->more() )
7931       {
7932         const SMDS_MeshElement* elem = invElemIt->next();
7933         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7934         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7935         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7936         bool isVolume = volume.Set( elem );
7937         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7938         if ( isVolume ) // --volume
7939           hasVolumes = true;
7940         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7941           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7942           if(elem->IsQuadratic()) {
7943             const SMDS_VtkFace* F =
7944               dynamic_cast<const SMDS_VtkFace*>(elem);
7945             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7946             // use special nodes iterator
7947             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7948             while( anIter->more() ) {
7949               nodes[ iNode ] = cast2Node(anIter->next());
7950               if ( nodes[ iNode++ ] == prevSideNode )
7951                 iPrevNode = iNode - 1;
7952             }
7953           }
7954           else {
7955             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7956             while ( nIt->more() ) {
7957               nodes[ iNode ] = cast2Node( nIt->next() );
7958               if ( nodes[ iNode++ ] == prevSideNode )
7959                 iPrevNode = iNode - 1;
7960             }
7961           }
7962           // there are 2 links to check
7963           nbNodes = 2;
7964         }
7965         else // --edge
7966           continue;
7967         // loop on links, to be precise, on the second node of links
7968         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7969           const SMDS_MeshNode* n = nodes[ iNode ];
7970           if ( isVolume ) {
7971             if ( !volume.IsLinked( n, prevSideNode ))
7972               continue;
7973           }
7974           else {
7975             if ( iNode ) // a node before prevSideNode
7976               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7977             else         // a node after prevSideNode
7978               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7979           }
7980           // check if this link was already used
7981           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7982           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7983           if (!isJustChecked &&
7984               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7985           {
7986             // test a link geometrically
7987             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7988             bool linkIsBetter = false;
7989             double dot = 0.0, dist = 0.0;
7990             if ( searchByDir ) { // choose most co-directed link
7991               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7992               linkIsBetter = ( dot > maxDot );
7993             }
7994             else { // choose link with the node closest to bordPos
7995               dist = ( nextXYZ - bordPos ).SquareModulus();
7996               linkIsBetter = ( dist < minDist );
7997             }
7998             if ( linkIsBetter ) {
7999               maxDot = dot;
8000               minDist = dist;
8001               linkID = iLink;
8002               sideNode = n;
8003               sideElem = elem;
8004             }
8005           }
8006         }
8007       } // loop on inverse elements of prevSideNode
8008
8009       if ( !sideNode ) {
8010         MESSAGE(" Cant find path by links of the Side 2 ");
8011         return SEW_BAD_SIDE_NODES;
8012       }
8013       sideNodes.push_back( sideNode );
8014       sideElems.push_back( sideElem );
8015       foundSideLinkIDs.insert ( linkID );
8016       prevSideNode = sideNode;
8017
8018       if ( *nBordIt == theBordLastNode )
8019         searchByDir = false;
8020       else {
8021         // find the next border link to compare with
8022         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8023         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8024         // move to next border node if sideNode is before forward border node (bordPos)
8025         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8026           prevBordNode = *nBordIt;
8027           nBordIt++;
8028           bordPos = nBordXYZ[ *nBordIt ];
8029           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8030           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8031         }
8032       }
8033     }
8034     while ( sideNode != theSideSecondNode );
8035
8036     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8037       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8038       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8039     }
8040   } // end nodes search on the side 2
8041
8042   // ============================
8043   // sew the border to the side 2
8044   // ============================
8045
8046   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8047   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8048
8049   TListOfListOfNodes nodeGroupsToMerge;
8050   if ( nbNodes[0] == nbNodes[1] ||
8051        ( theSideIsFreeBorder && !theSideThirdNode)) {
8052
8053     // all nodes are to be merged
8054
8055     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8056          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8057          nIt[0]++, nIt[1]++ )
8058     {
8059       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8060       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8061       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8062     }
8063   }
8064   else {
8065
8066     // insert new nodes into the border and the side to get equal nb of segments
8067
8068     // get normalized parameters of nodes on the borders
8069     //double param[ 2 ][ maxNbNodes ];
8070     double* param[ 2 ];
8071     param[0] = new double [ maxNbNodes ];
8072     param[1] = new double [ maxNbNodes ];
8073     int iNode, iBord;
8074     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8075       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8076       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8077       const SMDS_MeshNode* nPrev = *nIt;
8078       double bordLength = 0;
8079       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8080         const SMDS_MeshNode* nCur = *nIt;
8081         gp_XYZ segment (nCur->X() - nPrev->X(),
8082                         nCur->Y() - nPrev->Y(),
8083                         nCur->Z() - nPrev->Z());
8084         double segmentLen = segment.Modulus();
8085         bordLength += segmentLen;
8086         param[ iBord ][ iNode ] = bordLength;
8087         nPrev = nCur;
8088       }
8089       // normalize within [0,1]
8090       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8091         param[ iBord ][ iNode ] /= bordLength;
8092       }
8093     }
8094
8095     // loop on border segments
8096     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8097     int i[ 2 ] = { 0, 0 };
8098     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8099     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8100
8101     TElemOfNodeListMap insertMap;
8102     TElemOfNodeListMap::iterator insertMapIt;
8103     // insertMap is
8104     // key:   elem to insert nodes into
8105     // value: 2 nodes to insert between + nodes to be inserted
8106     do {
8107       bool next[ 2 ] = { false, false };
8108
8109       // find min adjacent segment length after sewing
8110       double nextParam = 10., prevParam = 0;
8111       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8112         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8113           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8114         if ( i[ iBord ] > 0 )
8115           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8116       }
8117       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8118       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8119       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8120
8121       // choose to insert or to merge nodes
8122       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8123       if ( Abs( du ) <= minSegLen * 0.2 ) {
8124         // merge
8125         // ------
8126         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8127         const SMDS_MeshNode* n0 = *nIt[0];
8128         const SMDS_MeshNode* n1 = *nIt[1];
8129         nodeGroupsToMerge.back().push_back( n1 );
8130         nodeGroupsToMerge.back().push_back( n0 );
8131         // position of node of the border changes due to merge
8132         param[ 0 ][ i[0] ] += du;
8133         // move n1 for the sake of elem shape evaluation during insertion.
8134         // n1 will be removed by MergeNodes() anyway
8135         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8136         next[0] = next[1] = true;
8137       }
8138       else {
8139         // insert
8140         // ------
8141         int intoBord = ( du < 0 ) ? 0 : 1;
8142         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8143         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8144         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8145         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8146         if ( intoBord == 1 ) {
8147           // move node of the border to be on a link of elem of the side
8148           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8149           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8150           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8151           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8152           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8153         }
8154         insertMapIt = insertMap.find( elem );
8155         bool notFound = ( insertMapIt == insertMap.end() );
8156         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8157         if ( otherLink ) {
8158           // insert into another link of the same element:
8159           // 1. perform insertion into the other link of the elem
8160           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8161           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8162           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8163           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8164           // 2. perform insertion into the link of adjacent faces
8165           while (true) {
8166             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8167             if ( adjElem )
8168               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8169             else
8170               break;
8171           }
8172           if (toCreatePolyedrs) {
8173             // perform insertion into the links of adjacent volumes
8174             UpdateVolumes(n12, n22, nodeList);
8175           }
8176           // 3. find an element appeared on n1 and n2 after the insertion
8177           insertMap.erase( elem );
8178           elem = findAdjacentFace( n1, n2, 0 );
8179         }
8180         if ( notFound || otherLink ) {
8181           // add element and nodes of the side into the insertMap
8182           insertMapIt = insertMap.insert
8183             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8184           (*insertMapIt).second.push_back( n1 );
8185           (*insertMapIt).second.push_back( n2 );
8186         }
8187         // add node to be inserted into elem
8188         (*insertMapIt).second.push_back( nIns );
8189         next[ 1 - intoBord ] = true;
8190       }
8191
8192       // go to the next segment
8193       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8194         if ( next[ iBord ] ) {
8195           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8196             eIt[ iBord ]++;
8197           nPrev[ iBord ] = *nIt[ iBord ];
8198           nIt[ iBord ]++; i[ iBord ]++;
8199         }
8200       }
8201     }
8202     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8203
8204     // perform insertion of nodes into elements
8205
8206     for (insertMapIt = insertMap.begin();
8207          insertMapIt != insertMap.end();
8208          insertMapIt++ )
8209     {
8210       const SMDS_MeshElement* elem = (*insertMapIt).first;
8211       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8212       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8213       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8214
8215       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8216
8217       if ( !theSideIsFreeBorder ) {
8218         // look for and insert nodes into the faces adjacent to elem
8219         while (true) {
8220           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8221           if ( adjElem )
8222             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8223           else
8224             break;
8225         }
8226       }
8227       if (toCreatePolyedrs) {
8228         // perform insertion into the links of adjacent volumes
8229         UpdateVolumes(n1, n2, nodeList);
8230       }
8231     }
8232
8233     delete param[0];
8234     delete param[1];
8235   } // end: insert new nodes
8236
8237   MergeNodes ( nodeGroupsToMerge );
8238
8239   return aResult;
8240 }
8241
8242 //=======================================================================
8243 //function : InsertNodesIntoLink
8244 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8245 //           and theBetweenNode2 and split theElement
8246 //=======================================================================
8247
8248 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8249                                            const SMDS_MeshNode*        theBetweenNode1,
8250                                            const SMDS_MeshNode*        theBetweenNode2,
8251                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8252                                            const bool                  toCreatePoly)
8253 {
8254   if ( theFace->GetType() != SMDSAbs_Face ) return;
8255
8256   // find indices of 2 link nodes and of the rest nodes
8257   int iNode = 0, il1, il2, i3, i4;
8258   il1 = il2 = i3 = i4 = -1;
8259   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8260   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8261
8262   if(theFace->IsQuadratic()) {
8263     const SMDS_VtkFace* F =
8264       dynamic_cast<const SMDS_VtkFace*>(theFace);
8265     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8266     // use special nodes iterator
8267     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8268     while( anIter->more() ) {
8269       const SMDS_MeshNode* n = cast2Node(anIter->next());
8270       if ( n == theBetweenNode1 )
8271         il1 = iNode;
8272       else if ( n == theBetweenNode2 )
8273         il2 = iNode;
8274       else if ( i3 < 0 )
8275         i3 = iNode;
8276       else
8277         i4 = iNode;
8278       nodes[ iNode++ ] = n;
8279     }
8280   }
8281   else {
8282     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8283     while ( nodeIt->more() ) {
8284       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8285       if ( n == theBetweenNode1 )
8286         il1 = iNode;
8287       else if ( n == theBetweenNode2 )
8288         il2 = iNode;
8289       else if ( i3 < 0 )
8290         i3 = iNode;
8291       else
8292         i4 = iNode;
8293       nodes[ iNode++ ] = n;
8294     }
8295   }
8296   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8297     return ;
8298
8299   // arrange link nodes to go one after another regarding the face orientation
8300   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8301   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8302   if ( reverse ) {
8303     iNode = il1;
8304     il1 = il2;
8305     il2 = iNode;
8306     aNodesToInsert.reverse();
8307   }
8308   // check that not link nodes of a quadrangles are in good order
8309   int nbFaceNodes = theFace->NbNodes();
8310   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8311     iNode = i3;
8312     i3 = i4;
8313     i4 = iNode;
8314   }
8315
8316   if (toCreatePoly || theFace->IsPoly()) {
8317
8318     iNode = 0;
8319     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8320
8321     // add nodes of face up to first node of link
8322     bool isFLN = false;
8323
8324     if(theFace->IsQuadratic()) {
8325       const SMDS_VtkFace* F =
8326         dynamic_cast<const SMDS_VtkFace*>(theFace);
8327       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8328       // use special nodes iterator
8329       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8330       while( anIter->more()  && !isFLN ) {
8331         const SMDS_MeshNode* n = cast2Node(anIter->next());
8332         poly_nodes[iNode++] = n;
8333         if (n == nodes[il1]) {
8334           isFLN = true;
8335         }
8336       }
8337       // add nodes to insert
8338       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8339       for (; nIt != aNodesToInsert.end(); nIt++) {
8340         poly_nodes[iNode++] = *nIt;
8341       }
8342       // add nodes of face starting from last node of link
8343       while ( anIter->more() ) {
8344         poly_nodes[iNode++] = cast2Node(anIter->next());
8345       }
8346     }
8347     else {
8348       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8349       while ( nodeIt->more() && !isFLN ) {
8350         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8351         poly_nodes[iNode++] = n;
8352         if (n == nodes[il1]) {
8353           isFLN = true;
8354         }
8355       }
8356       // add nodes to insert
8357       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8358       for (; nIt != aNodesToInsert.end(); nIt++) {
8359         poly_nodes[iNode++] = *nIt;
8360       }
8361       // add nodes of face starting from last node of link
8362       while ( nodeIt->more() ) {
8363         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8364         poly_nodes[iNode++] = n;
8365       }
8366     }
8367
8368     // edit or replace the face
8369     SMESHDS_Mesh *aMesh = GetMeshDS();
8370
8371     if (theFace->IsPoly()) {
8372       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8373     }
8374     else {
8375       int aShapeId = FindShape( theFace );
8376
8377       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8378       myLastCreatedElems.Append(newElem);
8379       if ( aShapeId && newElem )
8380         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8381
8382       aMesh->RemoveElement(theFace);
8383     }
8384     return;
8385   }
8386
8387   SMESHDS_Mesh *aMesh = GetMeshDS();
8388   if( !theFace->IsQuadratic() ) {
8389
8390     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8391     int nbLinkNodes = 2 + aNodesToInsert.size();
8392     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8393     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8394     linkNodes[ 0 ] = nodes[ il1 ];
8395     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8396     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8397     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8398       linkNodes[ iNode++ ] = *nIt;
8399     }
8400     // decide how to split a quadrangle: compare possible variants
8401     // and choose which of splits to be a quadrangle
8402     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8403     if ( nbFaceNodes == 3 ) {
8404       iBestQuad = nbSplits;
8405       i4 = i3;
8406     }
8407     else if ( nbFaceNodes == 4 ) {
8408       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8409       double aBestRate = DBL_MAX;
8410       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8411         i1 = 0; i2 = 1;
8412         double aBadRate = 0;
8413         // evaluate elements quality
8414         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8415           if ( iSplit == iQuad ) {
8416             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8417                                    linkNodes[ i2++ ],
8418                                    nodes[ i3 ],
8419                                    nodes[ i4 ]);
8420             aBadRate += getBadRate( &quad, aCrit );
8421           }
8422           else {
8423             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8424                                    linkNodes[ i2++ ],
8425                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8426             aBadRate += getBadRate( &tria, aCrit );
8427           }
8428         }
8429         // choice
8430         if ( aBadRate < aBestRate ) {
8431           iBestQuad = iQuad;
8432           aBestRate = aBadRate;
8433         }
8434       }
8435     }
8436
8437     // create new elements
8438     int aShapeId = FindShape( theFace );
8439
8440     i1 = 0; i2 = 1;
8441     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8442       SMDS_MeshElement* newElem = 0;
8443       if ( iSplit == iBestQuad )
8444         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8445                                   linkNodes[ i2++ ],
8446                                   nodes[ i3 ],
8447                                   nodes[ i4 ]);
8448       else
8449         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8450                                   linkNodes[ i2++ ],
8451                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8452       myLastCreatedElems.Append(newElem);
8453       if ( aShapeId && newElem )
8454         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8455     }
8456
8457     // change nodes of theFace
8458     const SMDS_MeshNode* newNodes[ 4 ];
8459     newNodes[ 0 ] = linkNodes[ i1 ];
8460     newNodes[ 1 ] = linkNodes[ i2 ];
8461     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8462     newNodes[ 3 ] = nodes[ i4 ];
8463     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8464     const SMDS_MeshElement* newElem = 0;
8465     if (iSplit == iBestQuad)
8466       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8467     else
8468       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8469     myLastCreatedElems.Append(newElem);
8470     if ( aShapeId && newElem )
8471       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8472 } // end if(!theFace->IsQuadratic())
8473   else { // theFace is quadratic
8474     // we have to split theFace on simple triangles and one simple quadrangle
8475     int tmp = il1/2;
8476     int nbshift = tmp*2;
8477     // shift nodes in nodes[] by nbshift
8478     int i,j;
8479     for(i=0; i<nbshift; i++) {
8480       const SMDS_MeshNode* n = nodes[0];
8481       for(j=0; j<nbFaceNodes-1; j++) {
8482         nodes[j] = nodes[j+1];
8483       }
8484       nodes[nbFaceNodes-1] = n;
8485     }
8486     il1 = il1 - nbshift;
8487     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8488     //   n0      n1     n2    n0      n1     n2
8489     //     +-----+-----+        +-----+-----+
8490     //      \         /         |           |
8491     //       \       /          |           |
8492     //      n5+     +n3       n7+           +n3
8493     //         \   /            |           |
8494     //          \ /             |           |
8495     //           +              +-----+-----+
8496     //           n4           n6      n5     n4
8497
8498     // create new elements
8499     int aShapeId = FindShape( theFace );
8500
8501     int n1,n2,n3;
8502     if(nbFaceNodes==6) { // quadratic triangle
8503       SMDS_MeshElement* newElem =
8504         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8505       myLastCreatedElems.Append(newElem);
8506       if ( aShapeId && newElem )
8507         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8508       if(theFace->IsMediumNode(nodes[il1])) {
8509         // create quadrangle
8510         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8511         myLastCreatedElems.Append(newElem);
8512         if ( aShapeId && newElem )
8513           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8514         n1 = 1;
8515         n2 = 2;
8516         n3 = 3;
8517       }
8518       else {
8519         // create quadrangle
8520         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8521         myLastCreatedElems.Append(newElem);
8522         if ( aShapeId && newElem )
8523           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8524         n1 = 0;
8525         n2 = 1;
8526         n3 = 5;
8527       }
8528     }
8529     else { // nbFaceNodes==8 - quadratic quadrangle
8530       SMDS_MeshElement* newElem =
8531         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8532       myLastCreatedElems.Append(newElem);
8533       if ( aShapeId && newElem )
8534         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8535       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8536       myLastCreatedElems.Append(newElem);
8537       if ( aShapeId && newElem )
8538         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8539       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8540       myLastCreatedElems.Append(newElem);
8541       if ( aShapeId && newElem )
8542         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8543       if(theFace->IsMediumNode(nodes[il1])) {
8544         // create quadrangle
8545         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8546         myLastCreatedElems.Append(newElem);
8547         if ( aShapeId && newElem )
8548           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8549         n1 = 1;
8550         n2 = 2;
8551         n3 = 3;
8552       }
8553       else {
8554         // create quadrangle
8555         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8556         myLastCreatedElems.Append(newElem);
8557         if ( aShapeId && newElem )
8558           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8559         n1 = 0;
8560         n2 = 1;
8561         n3 = 7;
8562       }
8563     }
8564     // create needed triangles using n1,n2,n3 and inserted nodes
8565     int nbn = 2 + aNodesToInsert.size();
8566     //const SMDS_MeshNode* aNodes[nbn];
8567     vector<const SMDS_MeshNode*> aNodes(nbn);
8568     aNodes[0] = nodes[n1];
8569     aNodes[nbn-1] = nodes[n2];
8570     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8571     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8572       aNodes[iNode++] = *nIt;
8573     }
8574     for(i=1; i<nbn; i++) {
8575       SMDS_MeshElement* newElem =
8576         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8577       myLastCreatedElems.Append(newElem);
8578       if ( aShapeId && newElem )
8579         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8580     }
8581   }
8582   // remove old face
8583   aMesh->RemoveElement(theFace);
8584 }
8585
8586 //=======================================================================
8587 //function : UpdateVolumes
8588 //purpose  :
8589 //=======================================================================
8590 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8591                                       const SMDS_MeshNode*        theBetweenNode2,
8592                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8593 {
8594   myLastCreatedElems.Clear();
8595   myLastCreatedNodes.Clear();
8596
8597   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8598   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8599     const SMDS_MeshElement* elem = invElemIt->next();
8600
8601     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8602     SMDS_VolumeTool aVolume (elem);
8603     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8604       continue;
8605
8606     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8607     int iface, nbFaces = aVolume.NbFaces();
8608     vector<const SMDS_MeshNode *> poly_nodes;
8609     vector<int> quantities (nbFaces);
8610
8611     for (iface = 0; iface < nbFaces; iface++) {
8612       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8613       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8614       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8615
8616       for (int inode = 0; inode < nbFaceNodes; inode++) {
8617         poly_nodes.push_back(faceNodes[inode]);
8618
8619         if (nbInserted == 0) {
8620           if (faceNodes[inode] == theBetweenNode1) {
8621             if (faceNodes[inode + 1] == theBetweenNode2) {
8622               nbInserted = theNodesToInsert.size();
8623
8624               // add nodes to insert
8625               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8626               for (; nIt != theNodesToInsert.end(); nIt++) {
8627                 poly_nodes.push_back(*nIt);
8628               }
8629             }
8630           }
8631           else if (faceNodes[inode] == theBetweenNode2) {
8632             if (faceNodes[inode + 1] == theBetweenNode1) {
8633               nbInserted = theNodesToInsert.size();
8634
8635               // add nodes to insert in reversed order
8636               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8637               nIt--;
8638               for (; nIt != theNodesToInsert.begin(); nIt--) {
8639                 poly_nodes.push_back(*nIt);
8640               }
8641               poly_nodes.push_back(*nIt);
8642             }
8643           }
8644           else {
8645           }
8646         }
8647       }
8648       quantities[iface] = nbFaceNodes + nbInserted;
8649     }
8650
8651     // Replace or update the volume
8652     SMESHDS_Mesh *aMesh = GetMeshDS();
8653
8654     if (elem->IsPoly()) {
8655       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8656
8657     }
8658     else {
8659       int aShapeId = FindShape( elem );
8660
8661       SMDS_MeshElement* newElem =
8662         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8663       myLastCreatedElems.Append(newElem);
8664       if (aShapeId && newElem)
8665         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8666
8667       aMesh->RemoveElement(elem);
8668     }
8669   }
8670 }
8671
8672 namespace
8673 {
8674   //================================================================================
8675   /*!
8676    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8677    */
8678   //================================================================================
8679
8680   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8681                            vector<const SMDS_MeshNode *> & nodes,
8682                            vector<int> &                   nbNodeInFaces )
8683   {
8684     nodes.clear();
8685     nbNodeInFaces.clear();
8686     SMDS_VolumeTool vTool ( elem );
8687     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8688     {
8689       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8690       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8691       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8692     }
8693   }
8694 }
8695
8696 //=======================================================================
8697 /*!
8698  * \brief Convert elements contained in a submesh to quadratic
8699  * \return int - nb of checked elements
8700  */
8701 //=======================================================================
8702
8703 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8704                                              SMESH_MesherHelper& theHelper,
8705                                              const bool          theForce3d)
8706 {
8707   int nbElem = 0;
8708   if( !theSm ) return nbElem;
8709
8710   vector<int> nbNodeInFaces;
8711   vector<const SMDS_MeshNode *> nodes;
8712   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8713   while(ElemItr->more())
8714   {
8715     nbElem++;
8716     const SMDS_MeshElement* elem = ElemItr->next();
8717     if( !elem ) continue;
8718
8719     // analyse a necessity of conversion
8720     const SMDSAbs_ElementType aType = elem->GetType();
8721     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8722       continue;
8723     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8724     bool hasCentralNodes = false;
8725     if ( elem->IsQuadratic() )
8726     {
8727       bool alreadyOK;
8728       switch ( aGeomType ) {
8729       case SMDSEntity_Quad_Triangle:
8730       case SMDSEntity_Quad_Quadrangle:
8731       case SMDSEntity_Quad_Hexa:
8732         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8733
8734       case SMDSEntity_BiQuad_Triangle:
8735       case SMDSEntity_BiQuad_Quadrangle:
8736       case SMDSEntity_TriQuad_Hexa:
8737         alreadyOK = theHelper.GetIsBiQuadratic();
8738         hasCentralNodes = true;
8739         break;
8740       default:
8741         alreadyOK = true;
8742       }
8743       // take into account already present modium nodes
8744       switch ( aType ) {
8745       case SMDSAbs_Volume:
8746         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8747       case SMDSAbs_Face:
8748         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8749       case SMDSAbs_Edge:
8750         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8751       default:;
8752       }
8753       if ( alreadyOK )
8754         continue;
8755     }
8756     // get elem data needed to re-create it
8757     //
8758     const int id      = elem->GetID();
8759     const int nbNodes = elem->NbCornerNodes();
8760     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8761     if ( aGeomType == SMDSEntity_Polyhedra )
8762       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8763     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8764       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8765
8766     // remove a linear element
8767     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8768
8769     // remove central nodes of biquadratic elements (biquad->quad convertion)
8770     if ( hasCentralNodes )
8771       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8772         if ( nodes[i]->NbInverseElements() == 0 )
8773           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8774
8775     const SMDS_MeshElement* NewElem = 0;
8776
8777     switch( aType )
8778     {
8779     case SMDSAbs_Edge :
8780       {
8781         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8782         break;
8783       }
8784     case SMDSAbs_Face :
8785       {
8786         switch(nbNodes)
8787         {
8788         case 3:
8789           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8790           break;
8791         case 4:
8792           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8793           break;
8794         default:
8795           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8796         }
8797         break;
8798       }
8799     case SMDSAbs_Volume :
8800       {
8801         switch( aGeomType )
8802         {
8803         case SMDSEntity_Tetra:
8804           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8805           break;
8806         case SMDSEntity_Pyramid:
8807           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8808           break;
8809         case SMDSEntity_Penta:
8810           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8811           break;
8812         case SMDSEntity_Hexa:
8813         case SMDSEntity_Quad_Hexa:
8814         case SMDSEntity_TriQuad_Hexa:
8815           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8816                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8817           break;
8818         case SMDSEntity_Hexagonal_Prism:
8819         default:
8820           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8821         }
8822         break;
8823       }
8824     default :
8825       continue;
8826     }
8827     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8828     if( NewElem && NewElem->getshapeId() < 1 )
8829       theSm->AddElement( NewElem );
8830   }
8831   return nbElem;
8832 }
8833 //=======================================================================
8834 //function : ConvertToQuadratic
8835 //purpose  :
8836 //=======================================================================
8837
8838 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8839 {
8840   SMESHDS_Mesh* meshDS = GetMeshDS();
8841
8842   SMESH_MesherHelper aHelper(*myMesh);
8843
8844   aHelper.SetIsQuadratic( true );
8845   aHelper.SetIsBiQuadratic( theToBiQuad );
8846   aHelper.SetElementsOnShape(true);
8847   aHelper.ToFixNodeParameters( true );
8848
8849   // convert elements assigned to sub-meshes
8850   int nbCheckedElems = 0;
8851   if ( myMesh->HasShapeToMesh() )
8852   {
8853     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8854     {
8855       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8856       while ( smIt->more() ) {
8857         SMESH_subMesh* sm = smIt->next();
8858         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8859           aHelper.SetSubShape( sm->GetSubShape() );
8860           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8861         }
8862       }
8863     }
8864   }
8865
8866   // convert elements NOT assigned to sub-meshes
8867   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8868   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8869   {
8870     aHelper.SetElementsOnShape(false);
8871     SMESHDS_SubMesh *smDS = 0;
8872
8873     // convert edges
8874     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8875     while( aEdgeItr->more() )
8876     {
8877       const SMDS_MeshEdge* edge = aEdgeItr->next();
8878       if ( !edge->IsQuadratic() )
8879       {
8880         int                  id = edge->GetID();
8881         const SMDS_MeshNode* n1 = edge->GetNode(0);
8882         const SMDS_MeshNode* n2 = edge->GetNode(1);
8883
8884         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8885
8886         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8887         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8888       }
8889       else
8890       {
8891         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8892       }
8893     }
8894
8895     // convert faces
8896     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8897     while( aFaceItr->more() )
8898     {
8899       const SMDS_MeshFace* face = aFaceItr->next();
8900       if ( !face ) continue;
8901       
8902       const SMDSAbs_EntityType type = face->GetEntityType();
8903       bool alreadyOK;
8904       switch( type )
8905       {
8906       case SMDSEntity_Quad_Triangle:
8907       case SMDSEntity_Quad_Quadrangle:
8908         alreadyOK = !theToBiQuad;
8909         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8910         break;
8911       case SMDSEntity_BiQuad_Triangle:
8912       case SMDSEntity_BiQuad_Quadrangle:
8913         alreadyOK = theToBiQuad;
8914         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8915         break;
8916       default: alreadyOK = false;
8917       }
8918       if ( alreadyOK )
8919         continue;
8920
8921       const int id = face->GetID();
8922       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8923
8924       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8925
8926       SMDS_MeshFace * NewFace = 0;
8927       switch( type )
8928       {
8929       case SMDSEntity_Triangle:
8930       case SMDSEntity_Quad_Triangle:
8931       case SMDSEntity_BiQuad_Triangle:
8932         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8933         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8934           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8935         break;
8936
8937       case SMDSEntity_Quadrangle:
8938       case SMDSEntity_Quad_Quadrangle:
8939       case SMDSEntity_BiQuad_Quadrangle:
8940         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8941         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8942           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8943         break;
8944
8945       default:;
8946         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8947       }
8948       ReplaceElemInGroups( face, NewFace, GetMeshDS());
8949     }
8950
8951     // convert volumes
8952     vector<int> nbNodeInFaces;
8953     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8954     while(aVolumeItr->more())
8955     {
8956       const SMDS_MeshVolume* volume = aVolumeItr->next();
8957       if ( !volume ) continue;
8958
8959       const SMDSAbs_EntityType type = volume->GetEntityType();
8960       if ( volume->IsQuadratic() )
8961       {
8962         bool alreadyOK;
8963         switch ( type )
8964         {
8965         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
8966         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8967         default:                      alreadyOK = true;
8968         }
8969         if ( alreadyOK )
8970         {
8971           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8972           continue;
8973         }
8974       }
8975       const int id = volume->GetID();
8976       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8977       if ( type == SMDSEntity_Polyhedra )
8978         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8979       else if ( type == SMDSEntity_Hexagonal_Prism )
8980         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8981
8982       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8983
8984       SMDS_MeshVolume * NewVolume = 0;
8985       switch ( type )
8986       {
8987       case SMDSEntity_Tetra:
8988         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8989         break;
8990       case SMDSEntity_Hexa:
8991       case SMDSEntity_Quad_Hexa:
8992       case SMDSEntity_TriQuad_Hexa:
8993         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8994                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8995         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8996           if ( nodes[i]->NbInverseElements() == 0 )
8997             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8998         break;
8999       case SMDSEntity_Pyramid:
9000         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9001                                       nodes[3], nodes[4], id, theForce3d);
9002         break;
9003       case SMDSEntity_Penta:
9004         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9005                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9006         break;
9007       case SMDSEntity_Hexagonal_Prism:
9008       default:
9009         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9010       }
9011       ReplaceElemInGroups(volume, NewVolume, meshDS);
9012     }
9013   }
9014
9015   if ( !theForce3d )
9016   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9017     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9018     // aHelper.FixQuadraticElements(myError);
9019     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9020   }
9021 }
9022
9023 //================================================================================
9024 /*!
9025  * \brief Makes given elements quadratic
9026  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9027  *  \param theElements - elements to make quadratic
9028  */
9029 //================================================================================
9030
9031 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9032                                           TIDSortedElemSet& theElements,
9033                                           const bool        theToBiQuad)
9034 {
9035   if ( theElements.empty() ) return;
9036
9037   // we believe that all theElements are of the same type
9038   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9039
9040   // get all nodes shared by theElements
9041   TIDSortedNodeSet allNodes;
9042   TIDSortedElemSet::iterator eIt = theElements.begin();
9043   for ( ; eIt != theElements.end(); ++eIt )
9044     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9045
9046   // complete theElements with elements of lower dim whose all nodes are in allNodes
9047
9048   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9049   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9050   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9051   for ( ; nIt != allNodes.end(); ++nIt )
9052   {
9053     const SMDS_MeshNode* n = *nIt;
9054     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9055     while ( invIt->more() )
9056     {
9057       const SMDS_MeshElement*      e = invIt->next();
9058       const SMDSAbs_ElementType type = e->GetType();
9059       if ( e->IsQuadratic() )
9060       {
9061         quadAdjacentElems[ type ].insert( e );
9062
9063         bool alreadyOK;
9064         switch ( e->GetEntityType() ) {
9065         case SMDSEntity_Quad_Triangle:
9066         case SMDSEntity_Quad_Quadrangle:
9067         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9068         case SMDSEntity_BiQuad_Triangle:
9069         case SMDSEntity_BiQuad_Quadrangle:
9070         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9071         default:                           alreadyOK = true;
9072         }
9073         if ( alreadyOK )
9074           continue;
9075       }
9076       if ( type >= elemType )
9077         continue; // same type or more complex linear element
9078
9079       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9080         continue; // e is already checked
9081
9082       // check nodes
9083       bool allIn = true;
9084       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9085       while ( nodeIt->more() && allIn )
9086         allIn = allNodes.count( nodeIt->next() );
9087       if ( allIn )
9088         theElements.insert(e );
9089     }
9090   }
9091
9092   SMESH_MesherHelper helper(*myMesh);
9093   helper.SetIsQuadratic( true );
9094   helper.SetIsBiQuadratic( theToBiQuad );
9095
9096   // add links of quadratic adjacent elements to the helper
9097
9098   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9099     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9100           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9101     {
9102       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9103     }
9104   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9105     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9106           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9107     {
9108       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9109     }
9110   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9111     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9112           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9113     {
9114       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9115     }
9116
9117   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9118
9119   SMESHDS_Mesh*  meshDS = GetMeshDS();
9120   SMESHDS_SubMesh* smDS = 0;
9121   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9122   {
9123     const SMDS_MeshElement* elem = *eIt;
9124
9125     bool alreadyOK;
9126     int nbCentralNodes = 0;
9127     switch ( elem->GetEntityType() ) {
9128       // linear convertible
9129     case SMDSEntity_Edge:
9130     case SMDSEntity_Triangle:
9131     case SMDSEntity_Quadrangle:
9132     case SMDSEntity_Tetra:
9133     case SMDSEntity_Pyramid:
9134     case SMDSEntity_Hexa:
9135     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9136       // quadratic that can become bi-quadratic
9137     case SMDSEntity_Quad_Triangle:
9138     case SMDSEntity_Quad_Quadrangle:
9139     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9140       // bi-quadratic
9141     case SMDSEntity_BiQuad_Triangle:
9142     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9143     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9144       // the rest
9145     default:                           alreadyOK = true;
9146     }
9147     if ( alreadyOK ) continue;
9148
9149     const SMDSAbs_ElementType type = elem->GetType();
9150     const int                   id = elem->GetID();
9151     const int              nbNodes = elem->NbCornerNodes();
9152     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9153
9154     helper.SetSubShape( elem->getshapeId() );
9155
9156     if ( !smDS || !smDS->Contains( elem ))
9157       smDS = meshDS->MeshElements( elem->getshapeId() );
9158     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9159
9160     SMDS_MeshElement * newElem = 0;
9161     switch( nbNodes )
9162     {
9163     case 4: // cases for most frequently used element types go first (for optimization)
9164       if ( type == SMDSAbs_Volume )
9165         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9166       else
9167         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9168       break;
9169     case 8:
9170       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9171                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9172       break;
9173     case 3:
9174       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9175       break;
9176     case 2:
9177       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9178       break;
9179     case 5:
9180       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9181                                  nodes[4], id, theForce3d);
9182       break;
9183     case 6:
9184       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9185                                  nodes[4], nodes[5], id, theForce3d);
9186       break;
9187     default:;
9188     }
9189     ReplaceElemInGroups( elem, newElem, meshDS);
9190     if( newElem && smDS )
9191       smDS->AddElement( newElem );
9192
9193      // remove central nodes
9194     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9195       if ( nodes[i]->NbInverseElements() == 0 )
9196         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9197
9198   } // loop on theElements
9199
9200   if ( !theForce3d )
9201   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9202     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9203     // helper.FixQuadraticElements( myError );
9204     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9205   }
9206 }
9207
9208 //=======================================================================
9209 /*!
9210  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9211  * \return int - nb of checked elements
9212  */
9213 //=======================================================================
9214
9215 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9216                                      SMDS_ElemIteratorPtr theItr,
9217                                      const int            theShapeID)
9218 {
9219   int nbElem = 0;
9220   SMESHDS_Mesh* meshDS = GetMeshDS();
9221
9222   while( theItr->more() )
9223   {
9224     const SMDS_MeshElement* elem = theItr->next();
9225     nbElem++;
9226     if( elem && elem->IsQuadratic())
9227     {
9228       int id                    = elem->GetID();
9229       int nbCornerNodes         = elem->NbCornerNodes();
9230       SMDSAbs_ElementType aType = elem->GetType();
9231
9232       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9233
9234       //remove a quadratic element
9235       if ( !theSm || !theSm->Contains( elem ))
9236         theSm = meshDS->MeshElements( elem->getshapeId() );
9237       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9238
9239       // remove medium nodes
9240       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9241         if ( nodes[i]->NbInverseElements() == 0 )
9242           meshDS->RemoveFreeNode( nodes[i], theSm );
9243
9244       // add a linear element
9245       nodes.resize( nbCornerNodes );
9246       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9247       ReplaceElemInGroups(elem, newElem, meshDS);
9248       if( theSm && newElem )
9249         theSm->AddElement( newElem );
9250     }
9251   }
9252   return nbElem;
9253 }
9254
9255 //=======================================================================
9256 //function : ConvertFromQuadratic
9257 //purpose  :
9258 //=======================================================================
9259
9260 bool SMESH_MeshEditor::ConvertFromQuadratic()
9261 {
9262   int nbCheckedElems = 0;
9263   if ( myMesh->HasShapeToMesh() )
9264   {
9265     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9266     {
9267       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9268       while ( smIt->more() ) {
9269         SMESH_subMesh* sm = smIt->next();
9270         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9271           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9272       }
9273     }
9274   }
9275
9276   int totalNbElems =
9277     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9278   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9279   {
9280     SMESHDS_SubMesh *aSM = 0;
9281     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9282   }
9283
9284   return true;
9285 }
9286
9287 namespace
9288 {
9289   //================================================================================
9290   /*!
9291    * \brief Return true if all medium nodes of the element are in the node set
9292    */
9293   //================================================================================
9294
9295   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9296   {
9297     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9298       if ( !nodeSet.count( elem->GetNode(i) ))
9299         return false;
9300     return true;
9301   }
9302 }
9303
9304 //================================================================================
9305 /*!
9306  * \brief Makes given elements linear
9307  */
9308 //================================================================================
9309
9310 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9311 {
9312   if ( theElements.empty() ) return;
9313
9314   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9315   set<int> mediumNodeIDs;
9316   TIDSortedElemSet::iterator eIt = theElements.begin();
9317   for ( ; eIt != theElements.end(); ++eIt )
9318   {
9319     const SMDS_MeshElement* e = *eIt;
9320     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9321       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9322   }
9323
9324   // replace given elements by linear ones
9325   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9326   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9327
9328   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9329   // except those elements sharing medium nodes of quadratic element whose medium nodes
9330   // are not all in mediumNodeIDs
9331
9332   // get remaining medium nodes
9333   TIDSortedNodeSet mediumNodes;
9334   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9335   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9336     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9337       mediumNodes.insert( mediumNodes.end(), n );
9338
9339   // find more quadratic elements to convert
9340   TIDSortedElemSet moreElemsToConvert;
9341   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9342   for ( ; nIt != mediumNodes.end(); ++nIt )
9343   {
9344     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9345     while ( invIt->more() )
9346     {
9347       const SMDS_MeshElement* e = invIt->next();
9348       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9349       {
9350         // find a more complex element including e and
9351         // whose medium nodes are not in mediumNodes
9352         bool complexFound = false;
9353         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9354         {
9355           SMDS_ElemIteratorPtr invIt2 =
9356             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9357           while ( invIt2->more() )
9358           {
9359             const SMDS_MeshElement* eComplex = invIt2->next();
9360             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9361             {
9362               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9363               if ( nbCommonNodes == e->NbNodes())
9364               {
9365                 complexFound = true;
9366                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9367                 break;
9368               }
9369             }
9370           }
9371         }
9372         if ( !complexFound )
9373           moreElemsToConvert.insert( e );
9374       }
9375     }
9376   }
9377   elemIt = elemSetIterator( moreElemsToConvert );
9378   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9379 }
9380
9381 //=======================================================================
9382 //function : SewSideElements
9383 //purpose  :
9384 //=======================================================================
9385
9386 SMESH_MeshEditor::Sew_Error
9387 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9388                                    TIDSortedElemSet&    theSide2,
9389                                    const SMDS_MeshNode* theFirstNode1,
9390                                    const SMDS_MeshNode* theFirstNode2,
9391                                    const SMDS_MeshNode* theSecondNode1,
9392                                    const SMDS_MeshNode* theSecondNode2)
9393 {
9394   myLastCreatedElems.Clear();
9395   myLastCreatedNodes.Clear();
9396
9397   MESSAGE ("::::SewSideElements()");
9398   if ( theSide1.size() != theSide2.size() )
9399     return SEW_DIFF_NB_OF_ELEMENTS;
9400
9401   Sew_Error aResult = SEW_OK;
9402   // Algo:
9403   // 1. Build set of faces representing each side
9404   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9405   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9406
9407   // =======================================================================
9408   // 1. Build set of faces representing each side:
9409   // =======================================================================
9410   // a. build set of nodes belonging to faces
9411   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9412   // c. create temporary faces representing side of volumes if correspondent
9413   //    face does not exist
9414
9415   SMESHDS_Mesh* aMesh = GetMeshDS();
9416   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9417   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9418   TIDSortedElemSet             faceSet1, faceSet2;
9419   set<const SMDS_MeshElement*> volSet1,  volSet2;
9420   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9421   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9422   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9423   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9424   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9425   int iSide, iFace, iNode;
9426
9427   list<const SMDS_MeshElement* > tempFaceList;
9428   for ( iSide = 0; iSide < 2; iSide++ ) {
9429     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9430     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9431     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9432     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9433     set<const SMDS_MeshElement*>::iterator vIt;
9434     TIDSortedElemSet::iterator eIt;
9435     set<const SMDS_MeshNode*>::iterator    nIt;
9436
9437     // check that given nodes belong to given elements
9438     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9439     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9440     int firstIndex = -1, secondIndex = -1;
9441     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9442       const SMDS_MeshElement* elem = *eIt;
9443       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9444       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9445       if ( firstIndex > -1 && secondIndex > -1 ) break;
9446     }
9447     if ( firstIndex < 0 || secondIndex < 0 ) {
9448       // we can simply return until temporary faces created
9449       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9450     }
9451
9452     // -----------------------------------------------------------
9453     // 1a. Collect nodes of existing faces
9454     //     and build set of face nodes in order to detect missing
9455     //     faces corresponding to sides of volumes
9456     // -----------------------------------------------------------
9457
9458     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9459
9460     // loop on the given element of a side
9461     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9462       //const SMDS_MeshElement* elem = *eIt;
9463       const SMDS_MeshElement* elem = *eIt;
9464       if ( elem->GetType() == SMDSAbs_Face ) {
9465         faceSet->insert( elem );
9466         set <const SMDS_MeshNode*> faceNodeSet;
9467         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9468         while ( nodeIt->more() ) {
9469           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9470           nodeSet->insert( n );
9471           faceNodeSet.insert( n );
9472         }
9473         setOfFaceNodeSet.insert( faceNodeSet );
9474       }
9475       else if ( elem->GetType() == SMDSAbs_Volume )
9476         volSet->insert( elem );
9477     }
9478     // ------------------------------------------------------------------------------
9479     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9480     // ------------------------------------------------------------------------------
9481
9482     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9483       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9484       while ( fIt->more() ) { // loop on faces sharing a node
9485         const SMDS_MeshElement* f = fIt->next();
9486         if ( faceSet->find( f ) == faceSet->end() ) {
9487           // check if all nodes are in nodeSet and
9488           // complete setOfFaceNodeSet if they are
9489           set <const SMDS_MeshNode*> faceNodeSet;
9490           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9491           bool allInSet = true;
9492           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9493             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9494             if ( nodeSet->find( n ) == nodeSet->end() )
9495               allInSet = false;
9496             else
9497               faceNodeSet.insert( n );
9498           }
9499           if ( allInSet ) {
9500             faceSet->insert( f );
9501             setOfFaceNodeSet.insert( faceNodeSet );
9502           }
9503         }
9504       }
9505     }
9506
9507     // -------------------------------------------------------------------------
9508     // 1c. Create temporary faces representing sides of volumes if correspondent
9509     //     face does not exist
9510     // -------------------------------------------------------------------------
9511
9512     if ( !volSet->empty() ) {
9513       //int nodeSetSize = nodeSet->size();
9514
9515       // loop on given volumes
9516       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9517         SMDS_VolumeTool vol (*vIt);
9518         // loop on volume faces: find free faces
9519         // --------------------------------------
9520         list<const SMDS_MeshElement* > freeFaceList;
9521         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9522           if ( !vol.IsFreeFace( iFace ))
9523             continue;
9524           // check if there is already a face with same nodes in a face set
9525           const SMDS_MeshElement* aFreeFace = 0;
9526           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9527           int nbNodes = vol.NbFaceNodes( iFace );
9528           set <const SMDS_MeshNode*> faceNodeSet;
9529           vol.GetFaceNodes( iFace, faceNodeSet );
9530           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9531           if ( isNewFace ) {
9532             // no such a face is given but it still can exist, check it
9533             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9534             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9535           }
9536           if ( !aFreeFace ) {
9537             // create a temporary face
9538             if ( nbNodes == 3 ) {
9539               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9540               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9541             }
9542             else if ( nbNodes == 4 ) {
9543               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9544               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9545             }
9546             else {
9547               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9548               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9549               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9550             }
9551             if ( aFreeFace )
9552               tempFaceList.push_back( aFreeFace );
9553           }
9554
9555           if ( aFreeFace )
9556             freeFaceList.push_back( aFreeFace );
9557
9558         } // loop on faces of a volume
9559
9560         // choose one of several free faces of a volume
9561         // --------------------------------------------
9562         if ( freeFaceList.size() > 1 ) {
9563           // choose a face having max nb of nodes shared by other elems of a side
9564           int maxNbNodes = -1;
9565           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9566           while ( fIt != freeFaceList.end() ) { // loop on free faces
9567             int nbSharedNodes = 0;
9568             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9569             while ( nodeIt->more() ) { // loop on free face nodes
9570               const SMDS_MeshNode* n =
9571                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9572               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9573               while ( invElemIt->more() ) {
9574                 const SMDS_MeshElement* e = invElemIt->next();
9575                 nbSharedNodes += faceSet->count( e );
9576                 nbSharedNodes += elemSet->count( e );
9577               }
9578             }
9579             if ( nbSharedNodes > maxNbNodes ) {
9580               maxNbNodes = nbSharedNodes;
9581               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9582             }
9583             else if ( nbSharedNodes == maxNbNodes ) {
9584               fIt++;
9585             }
9586             else {
9587               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9588             }
9589           }
9590           if ( freeFaceList.size() > 1 )
9591           {
9592             // could not choose one face, use another way
9593             // choose a face most close to the bary center of the opposite side
9594             gp_XYZ aBC( 0., 0., 0. );
9595             set <const SMDS_MeshNode*> addedNodes;
9596             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9597             eIt = elemSet2->begin();
9598             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9599               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9600               while ( nodeIt->more() ) { // loop on free face nodes
9601                 const SMDS_MeshNode* n =
9602                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9603                 if ( addedNodes.insert( n ).second )
9604                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9605               }
9606             }
9607             aBC /= addedNodes.size();
9608             double minDist = DBL_MAX;
9609             fIt = freeFaceList.begin();
9610             while ( fIt != freeFaceList.end() ) { // loop on free faces
9611               double dist = 0;
9612               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9613               while ( nodeIt->more() ) { // loop on free face nodes
9614                 const SMDS_MeshNode* n =
9615                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9616                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9617                 dist += ( aBC - p ).SquareModulus();
9618               }
9619               if ( dist < minDist ) {
9620                 minDist = dist;
9621                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9622               }
9623               else
9624                 fIt = freeFaceList.erase( fIt++ );
9625             }
9626           }
9627         } // choose one of several free faces of a volume
9628
9629         if ( freeFaceList.size() == 1 ) {
9630           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9631           faceSet->insert( aFreeFace );
9632           // complete a node set with nodes of a found free face
9633           //           for ( iNode = 0; iNode < ; iNode++ )
9634           //             nodeSet->insert( fNodes[ iNode ] );
9635         }
9636
9637       } // loop on volumes of a side
9638
9639       //       // complete a set of faces if new nodes in a nodeSet appeared
9640       //       // ----------------------------------------------------------
9641       //       if ( nodeSetSize != nodeSet->size() ) {
9642       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9643       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9644       //           while ( fIt->more() ) { // loop on faces sharing a node
9645       //             const SMDS_MeshElement* f = fIt->next();
9646       //             if ( faceSet->find( f ) == faceSet->end() ) {
9647       //               // check if all nodes are in nodeSet and
9648       //               // complete setOfFaceNodeSet if they are
9649       //               set <const SMDS_MeshNode*> faceNodeSet;
9650       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9651       //               bool allInSet = true;
9652       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9653       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9654       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9655       //                   allInSet = false;
9656       //                 else
9657       //                   faceNodeSet.insert( n );
9658       //               }
9659       //               if ( allInSet ) {
9660       //                 faceSet->insert( f );
9661       //                 setOfFaceNodeSet.insert( faceNodeSet );
9662       //               }
9663       //             }
9664       //           }
9665       //         }
9666       //       }
9667     } // Create temporary faces, if there are volumes given
9668   } // loop on sides
9669
9670   if ( faceSet1.size() != faceSet2.size() ) {
9671     // delete temporary faces: they are in reverseElements of actual nodes
9672 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9673 //    while ( tmpFaceIt->more() )
9674 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9675 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9676 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9677 //      aMesh->RemoveElement(*tmpFaceIt);
9678     MESSAGE("Diff nb of faces");
9679     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9680   }
9681
9682   // ============================================================
9683   // 2. Find nodes to merge:
9684   //              bind a node to remove to a node to put instead
9685   // ============================================================
9686
9687   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9688   if ( theFirstNode1 != theFirstNode2 )
9689     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9690   if ( theSecondNode1 != theSecondNode2 )
9691     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9692
9693   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9694   set< long > linkIdSet; // links to process
9695   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9696
9697   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9698   list< NLink > linkList[2];
9699   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9700   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9701   // loop on links in linkList; find faces by links and append links
9702   // of the found faces to linkList
9703   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9704   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9705   {
9706     NLink link[] = { *linkIt[0], *linkIt[1] };
9707     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9708     if ( !linkIdSet.count( linkID ) )
9709       continue;
9710
9711     // by links, find faces in the face sets,
9712     // and find indices of link nodes in the found faces;
9713     // in a face set, there is only one or no face sharing a link
9714     // ---------------------------------------------------------------
9715
9716     const SMDS_MeshElement* face[] = { 0, 0 };
9717     vector<const SMDS_MeshNode*> fnodes[2];
9718     int iLinkNode[2][2];
9719     TIDSortedElemSet avoidSet;
9720     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9721       const SMDS_MeshNode* n1 = link[iSide].first;
9722       const SMDS_MeshNode* n2 = link[iSide].second;
9723       //cout << "Side " << iSide << " ";
9724       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9725       // find a face by two link nodes
9726       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9727                                                       *faceSetPtr[ iSide ], avoidSet,
9728                                                       &iLinkNode[iSide][0],
9729                                                       &iLinkNode[iSide][1] );
9730       if ( face[ iSide ])
9731       {
9732         //cout << " F " << face[ iSide]->GetID() <<endl;
9733         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9734         // put face nodes to fnodes
9735         if ( face[ iSide ]->IsQuadratic() )
9736         {
9737           // use interlaced nodes iterator
9738           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9739           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9740           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9741           while ( nIter->more() )
9742             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9743         }
9744         else
9745         {
9746           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9747                                   face[ iSide ]->end_nodes() );
9748         }
9749         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9750       }
9751     }
9752
9753     // check similarity of elements of the sides
9754     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9755       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9756       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9757         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9758       }
9759       else {
9760         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9761       }
9762       break; // do not return because it's necessary to remove tmp faces
9763     }
9764
9765     // set nodes to merge
9766     // -------------------
9767
9768     if ( face[0] && face[1] )  {
9769       const int nbNodes = face[0]->NbNodes();
9770       if ( nbNodes != face[1]->NbNodes() ) {
9771         MESSAGE("Diff nb of face nodes");
9772         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9773         break; // do not return because it s necessary to remove tmp faces
9774       }
9775       bool reverse[] = { false, false }; // order of nodes in the link
9776       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9777         // analyse link orientation in faces
9778         int i1 = iLinkNode[ iSide ][ 0 ];
9779         int i2 = iLinkNode[ iSide ][ 1 ];
9780         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9781       }
9782       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9783       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9784       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9785       {
9786         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9787                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9788       }
9789
9790       // add other links of the faces to linkList
9791       // -----------------------------------------
9792
9793       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
9794         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9795         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9796         if ( !iter_isnew.second ) { // already in a set: no need to process
9797           linkIdSet.erase( iter_isnew.first );
9798         }
9799         else // new in set == encountered for the first time: add
9800         {
9801           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9802           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9803           linkList[0].push_back ( NLink( n1, n2 ));
9804           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9805         }
9806       }
9807     } // 2 faces found
9808
9809     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9810       break;
9811
9812   } // loop on link lists
9813
9814   if ( aResult == SEW_OK &&
9815        ( //linkIt[0] != linkList[0].end() ||
9816          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9817     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9818              " " << (faceSetPtr[1]->empty()));
9819     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9820   }
9821
9822   // ====================================================================
9823   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9824   // ====================================================================
9825
9826   // delete temporary faces
9827 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9828 //  while ( tmpFaceIt->more() )
9829 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9830   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9831   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9832     aMesh->RemoveElement(*tmpFaceIt);
9833
9834   if ( aResult != SEW_OK)
9835     return aResult;
9836
9837   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9838   // loop on nodes replacement map
9839   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9840   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9841     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9842       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9843       nodeIDsToRemove.push_back( nToRemove->GetID() );
9844       // loop on elements sharing nToRemove
9845       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9846       while ( invElemIt->more() ) {
9847         const SMDS_MeshElement* e = invElemIt->next();
9848         // get a new suite of nodes: make replacement
9849         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9850         vector< const SMDS_MeshNode*> nodes( nbNodes );
9851         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9852         while ( nIt->more() ) {
9853           const SMDS_MeshNode* n =
9854             static_cast<const SMDS_MeshNode*>( nIt->next() );
9855           nnIt = nReplaceMap.find( n );
9856           if ( nnIt != nReplaceMap.end() ) {
9857             nbReplaced++;
9858             n = (*nnIt).second;
9859           }
9860           nodes[ i++ ] = n;
9861         }
9862         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9863         //         elemIDsToRemove.push_back( e->GetID() );
9864         //       else
9865         if ( nbReplaced )
9866           {
9867             SMDSAbs_ElementType etyp = e->GetType();
9868             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9869             if (newElem)
9870               {
9871                 myLastCreatedElems.Append(newElem);
9872                 AddToSameGroups(newElem, e, aMesh);
9873                 int aShapeId = e->getshapeId();
9874                 if ( aShapeId )
9875                   {
9876                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
9877                   }
9878               }
9879             aMesh->RemoveElement(e);
9880           }
9881       }
9882     }
9883
9884   Remove( nodeIDsToRemove, true );
9885
9886   return aResult;
9887 }
9888
9889 //================================================================================
9890 /*!
9891  * \brief Find corresponding nodes in two sets of faces
9892  * \param theSide1 - first face set
9893  * \param theSide2 - second first face
9894  * \param theFirstNode1 - a boundary node of set 1
9895  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9896  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9897  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9898  * \param nReplaceMap - output map of corresponding nodes
9899  * \return bool  - is a success or not
9900  */
9901 //================================================================================
9902
9903 #ifdef _DEBUG_
9904 //#define DEBUG_MATCHING_NODES
9905 #endif
9906
9907 SMESH_MeshEditor::Sew_Error
9908 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9909                                     set<const SMDS_MeshElement*>& theSide2,
9910                                     const SMDS_MeshNode*          theFirstNode1,
9911                                     const SMDS_MeshNode*          theFirstNode2,
9912                                     const SMDS_MeshNode*          theSecondNode1,
9913                                     const SMDS_MeshNode*          theSecondNode2,
9914                                     TNodeNodeMap &                nReplaceMap)
9915 {
9916   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9917
9918   nReplaceMap.clear();
9919   if ( theFirstNode1 != theFirstNode2 )
9920     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9921   if ( theSecondNode1 != theSecondNode2 )
9922     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9923
9924   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9925   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9926
9927   list< NLink > linkList[2];
9928   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9929   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9930
9931   // loop on links in linkList; find faces by links and append links
9932   // of the found faces to linkList
9933   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9934   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9935     NLink link[] = { *linkIt[0], *linkIt[1] };
9936     if ( linkSet.find( link[0] ) == linkSet.end() )
9937       continue;
9938
9939     // by links, find faces in the face sets,
9940     // and find indices of link nodes in the found faces;
9941     // in a face set, there is only one or no face sharing a link
9942     // ---------------------------------------------------------------
9943
9944     const SMDS_MeshElement* face[] = { 0, 0 };
9945     list<const SMDS_MeshNode*> notLinkNodes[2];
9946     //bool reverse[] = { false, false }; // order of notLinkNodes
9947     int nbNodes[2];
9948     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9949     {
9950       const SMDS_MeshNode* n1 = link[iSide].first;
9951       const SMDS_MeshNode* n2 = link[iSide].second;
9952       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9953       set< const SMDS_MeshElement* > facesOfNode1;
9954       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9955       {
9956         // during a loop of the first node, we find all faces around n1,
9957         // during a loop of the second node, we find one face sharing both n1 and n2
9958         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9959         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9960         while ( fIt->more() ) { // loop on faces sharing a node
9961           const SMDS_MeshElement* f = fIt->next();
9962           if (faceSet->find( f ) != faceSet->end() && // f is in face set
9963               ! facesOfNode1.insert( f ).second ) // f encounters twice
9964           {
9965             if ( face[ iSide ] ) {
9966               MESSAGE( "2 faces per link " );
9967               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9968             }
9969             face[ iSide ] = f;
9970             faceSet->erase( f );
9971
9972             // get not link nodes
9973             int nbN = f->NbNodes();
9974             if ( f->IsQuadratic() )
9975               nbN /= 2;
9976             nbNodes[ iSide ] = nbN;
9977             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9978             int i1 = f->GetNodeIndex( n1 );
9979             int i2 = f->GetNodeIndex( n2 );
9980             int iEnd = nbN, iBeg = -1, iDelta = 1;
9981             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9982             if ( reverse ) {
9983               std::swap( iEnd, iBeg ); iDelta = -1;
9984             }
9985             int i = i2;
9986             while ( true ) {
9987               i += iDelta;
9988               if ( i == iEnd ) i = iBeg + iDelta;
9989               if ( i == i1 ) break;
9990               nodes.push_back ( f->GetNode( i ) );
9991             }
9992           }
9993         }
9994       }
9995     }
9996     // check similarity of elements of the sides
9997     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9998       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9999       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10000         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10001       }
10002       else {
10003         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10004       }
10005     }
10006
10007     // set nodes to merge
10008     // -------------------
10009
10010     if ( face[0] && face[1] )  {
10011       if ( nbNodes[0] != nbNodes[1] ) {
10012         MESSAGE("Diff nb of face nodes");
10013         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10014       }
10015 #ifdef DEBUG_MATCHING_NODES
10016       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10017                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10018                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10019 #endif
10020       int nbN = nbNodes[0];
10021       {
10022         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10023         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10024         for ( int i = 0 ; i < nbN - 2; ++i ) {
10025 #ifdef DEBUG_MATCHING_NODES
10026           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10027 #endif
10028           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10029         }
10030       }
10031
10032       // add other links of the face 1 to linkList
10033       // -----------------------------------------
10034
10035       const SMDS_MeshElement* f0 = face[0];
10036       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10037       for ( int i = 0; i < nbN; i++ )
10038       {
10039         const SMDS_MeshNode* n2 = f0->GetNode( i );
10040         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10041           linkSet.insert( SMESH_TLink( n1, n2 ));
10042         if ( !iter_isnew.second ) { // already in a set: no need to process
10043           linkSet.erase( iter_isnew.first );
10044         }
10045         else // new in set == encountered for the first time: add
10046         {
10047 #ifdef DEBUG_MATCHING_NODES
10048           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10049                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10050 #endif
10051           linkList[0].push_back ( NLink( n1, n2 ));
10052           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10053         }
10054         n1 = n2;
10055       }
10056     } // 2 faces found
10057   } // loop on link lists
10058
10059   return SEW_OK;
10060 }
10061
10062 //================================================================================
10063 /*!
10064  * \brief Create elements equal (on same nodes) to given ones
10065  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10066  *              elements of the uppest dimension are duplicated.
10067  */
10068 //================================================================================
10069
10070 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10071 {
10072   CrearLastCreated();
10073   SMESHDS_Mesh* mesh = GetMeshDS();
10074
10075   // get an element type and an iterator over elements
10076
10077   SMDSAbs_ElementType type;
10078   SMDS_ElemIteratorPtr elemIt;
10079   vector< const SMDS_MeshElement* > allElems;
10080   if ( theElements.empty() )
10081   {
10082     if ( mesh->NbNodes() == 0 )
10083       return;
10084     // get most complex type
10085     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10086       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10087       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10088     };
10089     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10090       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10091       {
10092         type = types[i];
10093         break;
10094       }
10095     // put all elements in the vector <allElems>
10096     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10097     elemIt = mesh->elementsIterator( type );
10098     while ( elemIt->more() )
10099       allElems.push_back( elemIt->next());
10100     elemIt = elemSetIterator( allElems );
10101   }
10102   else
10103   {
10104     type = (*theElements.begin())->GetType();
10105     elemIt = elemSetIterator( theElements );
10106   }
10107
10108   // duplicate elements
10109
10110   if ( type == SMDSAbs_Ball )
10111   {
10112     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10113     while ( elemIt->more() )
10114     {
10115       const SMDS_MeshElement* elem = elemIt->next();
10116       if ( elem->GetType() != SMDSAbs_Ball )
10117         continue;
10118       if (( elem = mesh->AddBall( elem->GetNode(0),
10119                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10120         myLastCreatedElems.Append( elem );
10121     }
10122   }
10123   else
10124   {
10125     vector< const SMDS_MeshNode* > nodes;
10126     while ( elemIt->more() )
10127     {
10128       const SMDS_MeshElement* elem = elemIt->next();
10129       if ( elem->GetType() != type )
10130         continue;
10131
10132       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10133
10134       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10135       {
10136         std::vector<int> quantities =
10137           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10138         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10139       }
10140       else
10141       {
10142         AddElement( nodes, type, elem->IsPoly() );
10143         elem = 0; // myLastCreatedElems is already filled
10144       }
10145       if ( elem )
10146         myLastCreatedElems.Append( elem );
10147     }
10148   }
10149 }
10150
10151 //================================================================================
10152 /*!
10153   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10154   \param theElems - the list of elements (edges or faces) to be replicated
10155   The nodes for duplication could be found from these elements
10156   \param theNodesNot - list of nodes to NOT replicate
10157   \param theAffectedElems - the list of elements (cells and edges) to which the
10158   replicated nodes should be associated to.
10159   \return TRUE if operation has been completed successfully, FALSE otherwise
10160 */
10161 //================================================================================
10162
10163 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10164                                     const TIDSortedElemSet& theNodesNot,
10165                                     const TIDSortedElemSet& theAffectedElems )
10166 {
10167   myLastCreatedElems.Clear();
10168   myLastCreatedNodes.Clear();
10169
10170   if ( theElems.size() == 0 )
10171     return false;
10172
10173   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10174   if ( !aMeshDS )
10175     return false;
10176
10177   bool res = false;
10178   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10179   // duplicate elements and nodes
10180   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10181   // replce nodes by duplications
10182   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10183   return res;
10184 }
10185
10186 //================================================================================
10187 /*!
10188   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10189   \param theMeshDS - mesh instance
10190   \param theElems - the elements replicated or modified (nodes should be changed)
10191   \param theNodesNot - nodes to NOT replicate
10192   \param theNodeNodeMap - relation of old node to new created node
10193   \param theIsDoubleElem - flag os to replicate element or modify
10194   \return TRUE if operation has been completed successfully, FALSE otherwise
10195 */
10196 //================================================================================
10197
10198 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10199                                     const TIDSortedElemSet& theElems,
10200                                     const TIDSortedElemSet& theNodesNot,
10201                                     std::map< const SMDS_MeshNode*,
10202                                     const SMDS_MeshNode* >& theNodeNodeMap,
10203                                     const bool theIsDoubleElem )
10204 {
10205   MESSAGE("doubleNodes");
10206   // iterate on through element and duplicate them (by nodes duplication)
10207   bool res = false;
10208   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10209   for ( ;  elemItr != theElems.end(); ++elemItr )
10210   {
10211     const SMDS_MeshElement* anElem = *elemItr;
10212     if (!anElem)
10213       continue;
10214
10215     bool isDuplicate = false;
10216     // duplicate nodes to duplicate element
10217     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10218     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10219     int ind = 0;
10220     while ( anIter->more() )
10221     {
10222
10223       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10224       SMDS_MeshNode* aNewNode = aCurrNode;
10225       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10226         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10227       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10228       {
10229         // duplicate node
10230         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10231         copyPosition( aCurrNode, aNewNode );
10232         theNodeNodeMap[ aCurrNode ] = aNewNode;
10233         myLastCreatedNodes.Append( aNewNode );
10234       }
10235       isDuplicate |= (aCurrNode != aNewNode);
10236       newNodes[ ind++ ] = aNewNode;
10237     }
10238     if ( !isDuplicate )
10239       continue;
10240
10241     if ( theIsDoubleElem )
10242       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10243     else
10244       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10245
10246     res = true;
10247   }
10248   return res;
10249 }
10250
10251 //================================================================================
10252 /*!
10253   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10254   \param theNodes - identifiers of nodes to be doubled
10255   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10256   nodes. If list of element identifiers is empty then nodes are doubled but
10257   they not assigned to elements
10258   \return TRUE if operation has been completed successfully, FALSE otherwise
10259 */
10260 //================================================================================
10261
10262 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10263                                     const std::list< int >& theListOfModifiedElems )
10264 {
10265   MESSAGE("DoubleNodes");
10266   myLastCreatedElems.Clear();
10267   myLastCreatedNodes.Clear();
10268
10269   if ( theListOfNodes.size() == 0 )
10270     return false;
10271
10272   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10273   if ( !aMeshDS )
10274     return false;
10275
10276   // iterate through nodes and duplicate them
10277
10278   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10279
10280   std::list< int >::const_iterator aNodeIter;
10281   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10282   {
10283     int aCurr = *aNodeIter;
10284     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10285     if ( !aNode )
10286       continue;
10287
10288     // duplicate node
10289
10290     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10291     if ( aNewNode )
10292     {
10293       copyPosition( aNode, aNewNode );
10294       anOldNodeToNewNode[ aNode ] = aNewNode;
10295       myLastCreatedNodes.Append( aNewNode );
10296     }
10297   }
10298
10299   // Create map of new nodes for modified elements
10300
10301   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10302
10303   std::list< int >::const_iterator anElemIter;
10304   for ( anElemIter = theListOfModifiedElems.begin();
10305         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10306   {
10307     int aCurr = *anElemIter;
10308     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10309     if ( !anElem )
10310       continue;
10311
10312     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10313
10314     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10315     int ind = 0;
10316     while ( anIter->more() )
10317     {
10318       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10319       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10320       {
10321         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10322         aNodeArr[ ind++ ] = aNewNode;
10323       }
10324       else
10325         aNodeArr[ ind++ ] = aCurrNode;
10326     }
10327     anElemToNodes[ anElem ] = aNodeArr;
10328   }
10329
10330   // Change nodes of elements
10331
10332   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10333     anElemToNodesIter = anElemToNodes.begin();
10334   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10335   {
10336     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10337     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10338     if ( anElem )
10339       {
10340       MESSAGE("ChangeElementNodes");
10341       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10342       }
10343   }
10344
10345   return true;
10346 }
10347
10348 namespace {
10349
10350   //================================================================================
10351   /*!
10352   \brief Check if element located inside shape
10353   \return TRUE if IN or ON shape, FALSE otherwise
10354   */
10355   //================================================================================
10356
10357   template<class Classifier>
10358   bool isInside(const SMDS_MeshElement* theElem,
10359                 Classifier&             theClassifier,
10360                 const double            theTol)
10361   {
10362     gp_XYZ centerXYZ (0, 0, 0);
10363     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10364     while (aNodeItr->more())
10365       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10366
10367     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10368     theClassifier.Perform(aPnt, theTol);
10369     TopAbs_State aState = theClassifier.State();
10370     return (aState == TopAbs_IN || aState == TopAbs_ON );
10371   }
10372
10373   //================================================================================
10374   /*!
10375    * \brief Classifier of the 3D point on the TopoDS_Face
10376    *        with interaface suitable for isInside()
10377    */
10378   //================================================================================
10379
10380   struct _FaceClassifier
10381   {
10382     Extrema_ExtPS       _extremum;
10383     BRepAdaptor_Surface _surface;
10384     TopAbs_State        _state;
10385
10386     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10387     {
10388       _extremum.Initialize( _surface,
10389                             _surface.FirstUParameter(), _surface.LastUParameter(),
10390                             _surface.FirstVParameter(), _surface.LastVParameter(),
10391                             _surface.Tolerance(), _surface.Tolerance() );
10392     }
10393     void Perform(const gp_Pnt& aPnt, double theTol)
10394     {
10395       theTol *= theTol;
10396       _state = TopAbs_OUT;
10397       _extremum.Perform(aPnt);
10398       if ( _extremum.IsDone() )
10399         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10400           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10401     }
10402     TopAbs_State State() const
10403     {
10404       return _state;
10405     }
10406   };
10407 }
10408
10409 //================================================================================
10410 /*!
10411   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10412   This method is the first step of DoubleNodeElemGroupsInRegion.
10413   \param theElems - list of groups of elements (edges or faces) to be replicated
10414   \param theNodesNot - list of groups of nodes not to replicated
10415   \param theShape - shape to detect affected elements (element which geometric center
10416          located on or inside shape). If the shape is null, detection is done on faces orientations
10417          (select elements with a gravity center on the side given by faces normals).
10418          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10419          The replicated nodes should be associated to affected elements.
10420   \return groups of affected elements
10421   \sa DoubleNodeElemGroupsInRegion()
10422  */
10423 //================================================================================
10424
10425 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10426                                                    const TIDSortedElemSet& theNodesNot,
10427                                                    const TopoDS_Shape&     theShape,
10428                                                    TIDSortedElemSet&       theAffectedElems)
10429 {
10430   if ( theShape.IsNull() )
10431   {
10432     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10433     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10434     std::set<const SMDS_MeshElement*> edgesToCheck;
10435     alreadyCheckedNodes.clear();
10436     alreadyCheckedElems.clear();
10437     edgesToCheck.clear();
10438
10439     // --- iterates on elements to be replicated and get elements by back references from their nodes
10440
10441     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10442     int ielem;
10443     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10444     {
10445       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10446       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10447         continue;
10448       gp_XYZ normal;
10449       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10450       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10451       std::set<const SMDS_MeshNode*> nodesElem;
10452       nodesElem.clear();
10453       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10454       while ( nodeItr->more() )
10455       {
10456         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10457         nodesElem.insert(aNode);
10458       }
10459       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10460       for (; nodit != nodesElem.end(); nodit++)
10461       {
10462         MESSAGE("  noeud ");
10463         const SMDS_MeshNode* aNode = *nodit;
10464         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10465           continue;
10466         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10467           continue;
10468         alreadyCheckedNodes.insert(aNode);
10469         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10470         while ( backElemItr->more() )
10471         {
10472           MESSAGE("    backelem ");
10473           const SMDS_MeshElement* curElem = backElemItr->next();
10474           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10475             continue;
10476           if (theElems.find(curElem) != theElems.end())
10477             continue;
10478           alreadyCheckedElems.insert(curElem);
10479           double x=0, y=0, z=0;
10480           int nb = 0;
10481           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10482           while ( nodeItr2->more() )
10483           {
10484             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10485             x += anotherNode->X();
10486             y += anotherNode->Y();
10487             z += anotherNode->Z();
10488             nb++;
10489           }
10490           gp_XYZ p;
10491           p.SetCoord( x/nb -aNode->X(),
10492                       y/nb -aNode->Y(),
10493                       z/nb -aNode->Z() );
10494           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10495           if (normal*p > 0)
10496           {
10497             MESSAGE("    --- inserted")
10498             theAffectedElems.insert( curElem );
10499           }
10500           else if (curElem->GetType() == SMDSAbs_Edge)
10501             edgesToCheck.insert(curElem);
10502         }
10503       }
10504     }
10505     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10506     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10507     for( ; eit != edgesToCheck.end(); eit++)
10508     {
10509       bool onside = true;
10510       const SMDS_MeshElement* anEdge = *eit;
10511       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10512       while ( nodeItr->more() )
10513       {
10514         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10515         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10516         {
10517           onside = false;
10518           break;
10519         }
10520       }
10521       if (onside)
10522       {
10523         MESSAGE("    --- edge onside inserted")
10524         theAffectedElems.insert(anEdge);
10525       }
10526     }
10527   }
10528   else
10529   {
10530     const double aTol = Precision::Confusion();
10531     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10532     auto_ptr<_FaceClassifier>              aFaceClassifier;
10533     if ( theShape.ShapeType() == TopAbs_SOLID )
10534     {
10535       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10536       bsc3d->PerformInfinitePoint(aTol);
10537     }
10538     else if (theShape.ShapeType() == TopAbs_FACE )
10539     {
10540       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10541     }
10542
10543     // iterates on indicated elements and get elements by back references from their nodes
10544     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10545     int ielem;
10546     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10547     {
10548       MESSAGE("element " << ielem++);
10549       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10550       if (!anElem)
10551         continue;
10552       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10553       while ( nodeItr->more() )
10554       {
10555         MESSAGE("  noeud ");
10556         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10557         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10558           continue;
10559         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10560         while ( backElemItr->more() )
10561         {
10562           MESSAGE("    backelem ");
10563           const SMDS_MeshElement* curElem = backElemItr->next();
10564           if ( curElem && theElems.find(curElem) == theElems.end() &&
10565               ( bsc3d.get() ?
10566                 isInside( curElem, *bsc3d, aTol ) :
10567                 isInside( curElem, *aFaceClassifier, aTol )))
10568             theAffectedElems.insert( curElem );
10569         }
10570       }
10571     }
10572   }
10573   return true;
10574 }
10575
10576 //================================================================================
10577 /*!
10578   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10579   \param theElems - group of of elements (edges or faces) to be replicated
10580   \param theNodesNot - group of nodes not to replicate
10581   \param theShape - shape to detect affected elements (element which geometric center
10582   located on or inside shape).
10583   The replicated nodes should be associated to affected elements.
10584   \return TRUE if operation has been completed successfully, FALSE otherwise
10585 */
10586 //================================================================================
10587
10588 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10589                                             const TIDSortedElemSet& theNodesNot,
10590                                             const TopoDS_Shape&     theShape )
10591 {
10592   if ( theShape.IsNull() )
10593     return false;
10594
10595   const double aTol = Precision::Confusion();
10596   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10597   auto_ptr<_FaceClassifier>              aFaceClassifier;
10598   if ( theShape.ShapeType() == TopAbs_SOLID )
10599   {
10600     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10601     bsc3d->PerformInfinitePoint(aTol);
10602   }
10603   else if (theShape.ShapeType() == TopAbs_FACE )
10604   {
10605     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10606   }
10607
10608   // iterates on indicated elements and get elements by back references from their nodes
10609   TIDSortedElemSet anAffected;
10610   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10611   for ( ;  elemItr != theElems.end(); ++elemItr )
10612   {
10613     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10614     if (!anElem)
10615       continue;
10616
10617     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10618     while ( nodeItr->more() )
10619     {
10620       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10621       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10622         continue;
10623       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10624       while ( backElemItr->more() )
10625       {
10626         const SMDS_MeshElement* curElem = backElemItr->next();
10627         if ( curElem && theElems.find(curElem) == theElems.end() &&
10628              ( bsc3d.get() ?
10629                isInside( curElem, *bsc3d, aTol ) :
10630                isInside( curElem, *aFaceClassifier, aTol )))
10631           anAffected.insert( curElem );
10632       }
10633     }
10634   }
10635   return DoubleNodes( theElems, theNodesNot, anAffected );
10636 }
10637
10638 /*!
10639  *  \brief compute an oriented angle between two planes defined by four points.
10640  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10641  *  @param p0 base of the rotation axe
10642  *  @param p1 extremity of the rotation axe
10643  *  @param g1 belongs to the first plane
10644  *  @param g2 belongs to the second plane
10645  */
10646 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10647 {
10648 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10649 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10650 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10651 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10652   gp_Vec vref(p0, p1);
10653   gp_Vec v1(p0, g1);
10654   gp_Vec v2(p0, g2);
10655   gp_Vec n1 = vref.Crossed(v1);
10656   gp_Vec n2 = vref.Crossed(v2);
10657   try {
10658     return n2.AngleWithRef(n1, vref);
10659   }
10660   catch ( Standard_Failure ) {
10661   }
10662   return Max( v1.Magnitude(), v2.Magnitude() );
10663 }
10664
10665 /*!
10666  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10667  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10668  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10669  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10670  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10671  * 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.
10672  * 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.
10673  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10674  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10675  * \param theElems - list of groups of volumes, where a group of volume is a set of
10676  *        SMDS_MeshElements sorted by Id.
10677  * \param createJointElems - if TRUE, create the elements
10678  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10679  *        the boundary between \a theDomains and the rest mesh
10680  * \return TRUE if operation has been completed successfully, FALSE otherwise
10681  */
10682 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10683                                                      bool                                 createJointElems,
10684                                                      bool                                 onAllBoundaries)
10685 {
10686   MESSAGE("----------------------------------------------");
10687   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10688   MESSAGE("----------------------------------------------");
10689
10690   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10691   meshDS->BuildDownWardConnectivity(true);
10692   CHRONO(50);
10693   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10694
10695   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10696   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10697   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10698
10699   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10700   std::map<int,int>celldom; // cell vtkId --> domain
10701   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10702   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10703   faceDomains.clear();
10704   celldom.clear();
10705   cellDomains.clear();
10706   nodeDomains.clear();
10707   std::map<int,int> emptyMap;
10708   std::set<int> emptySet;
10709   emptyMap.clear();
10710
10711   MESSAGE(".. Number of domains :"<<theElems.size());
10712
10713   TIDSortedElemSet theRestDomElems;
10714   const int iRestDom  = -1;
10715   const int idom0     = onAllBoundaries ? iRestDom : 0;
10716   const int nbDomains = theElems.size();
10717
10718   // Check if the domains do not share an element
10719   for (int idom = 0; idom < nbDomains-1; idom++)
10720     {
10721 //       MESSAGE("... Check of domain #" << idom);
10722       const TIDSortedElemSet& domain = theElems[idom];
10723       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10724       for (; elemItr != domain.end(); ++elemItr)
10725         {
10726           const SMDS_MeshElement* anElem = *elemItr;
10727           int idombisdeb = idom + 1 ;
10728           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10729           {
10730             const TIDSortedElemSet& domainbis = theElems[idombis];
10731             if ( domainbis.count(anElem) )
10732             {
10733               MESSAGE(".... Domain #" << idom);
10734               MESSAGE(".... Domain #" << idombis);
10735               throw SALOME_Exception("The domains are not disjoint.");
10736               return false ;
10737             }
10738           }
10739         }
10740     }
10741
10742   for (int idom = 0; idom < nbDomains; idom++)
10743     {
10744
10745       // --- build a map (face to duplicate --> volume to modify)
10746       //     with all the faces shared by 2 domains (group of elements)
10747       //     and corresponding volume of this domain, for each shared face.
10748       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10749
10750       MESSAGE("... Neighbors of domain #" << idom);
10751       const TIDSortedElemSet& domain = theElems[idom];
10752       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10753       for (; elemItr != domain.end(); ++elemItr)
10754         {
10755           const SMDS_MeshElement* anElem = *elemItr;
10756           if (!anElem)
10757             continue;
10758           int vtkId = anElem->getVtkId();
10759           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
10760           int neighborsVtkIds[NBMAXNEIGHBORS];
10761           int downIds[NBMAXNEIGHBORS];
10762           unsigned char downTypes[NBMAXNEIGHBORS];
10763           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10764           for (int n = 0; n < nbNeighbors; n++)
10765             {
10766               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10767               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10768               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10769                 {
10770                   bool ok = false ;
10771                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10772                   {
10773                     // MESSAGE("Domain " << idombis);
10774                     const TIDSortedElemSet& domainbis = theElems[idombis];
10775                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10776                   }
10777                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10778                   {
10779                     DownIdType face(downIds[n], downTypes[n]);
10780                     if (!faceDomains[face].count(idom))
10781                       {
10782                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10783                         celldom[vtkId] = idom;
10784                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
10785                       }
10786                     if ( !ok )
10787                     {
10788                       theRestDomElems.insert( elem );
10789                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
10790                       celldom[neighborsVtkIds[n]] = iRestDom;
10791                     }
10792                   }
10793                 }
10794             }
10795         }
10796     }
10797
10798   //MESSAGE("Number of shared faces " << faceDomains.size());
10799   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10800
10801   // --- explore the shared faces domain by domain,
10802   //     explore the nodes of the face and see if they belong to a cell in the domain,
10803   //     which has only a node or an edge on the border (not a shared face)
10804
10805   for (int idomain = idom0; idomain < nbDomains; idomain++)
10806     {
10807       //MESSAGE("Domain " << idomain);
10808       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10809       itface = faceDomains.begin();
10810       for (; itface != faceDomains.end(); ++itface)
10811         {
10812           const std::map<int, int>& domvol = itface->second;
10813           if (!domvol.count(idomain))
10814             continue;
10815           DownIdType face = itface->first;
10816           //MESSAGE(" --- face " << face.cellId);
10817           std::set<int> oldNodes;
10818           oldNodes.clear();
10819           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10820           std::set<int>::iterator itn = oldNodes.begin();
10821           for (; itn != oldNodes.end(); ++itn)
10822             {
10823               int oldId = *itn;
10824               //MESSAGE("     node " << oldId);
10825               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10826               for (int i=0; i<l.ncells; i++)
10827                 {
10828                   int vtkId = l.cells[i];
10829                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10830                   if (!domain.count(anElem))
10831                     continue;
10832                   int vtkType = grid->GetCellType(vtkId);
10833                   int downId = grid->CellIdToDownId(vtkId);
10834                   if (downId < 0)
10835                     {
10836                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10837                       continue; // not OK at this stage of the algorithm:
10838                                 //no cells created after BuildDownWardConnectivity
10839                     }
10840                   DownIdType aCell(downId, vtkType);
10841                   cellDomains[aCell][idomain] = vtkId;
10842                   celldom[vtkId] = idomain;
10843                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
10844                 }
10845             }
10846         }
10847     }
10848
10849   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10850   //     for each shared face, get the nodes
10851   //     for each node, for each domain of the face, create a clone of the node
10852
10853   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10854   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10855   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
10856
10857   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10858   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10859   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10860
10861   MESSAGE(".. Duplication of the nodes");
10862   for (int idomain = idom0; idomain < nbDomains; idomain++)
10863     {
10864       itface = faceDomains.begin();
10865       for (; itface != faceDomains.end(); ++itface)
10866         {
10867           const std::map<int, int>& domvol = itface->second;
10868           if (!domvol.count(idomain))
10869             continue;
10870           DownIdType face = itface->first;
10871           //MESSAGE(" --- face " << face.cellId);
10872           std::set<int> oldNodes;
10873           oldNodes.clear();
10874           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10875           std::set<int>::iterator itn = oldNodes.begin();
10876           for (; itn != oldNodes.end(); ++itn)
10877             {
10878               int oldId = *itn;
10879               if (nodeDomains[oldId].empty())
10880                 {
10881                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10882                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
10883                 }
10884               std::map<int, int>::const_iterator itdom = domvol.begin();
10885               for (; itdom != domvol.end(); ++itdom)
10886                 {
10887                   int idom = itdom->first;
10888                   //MESSAGE("         domain " << idom);
10889                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
10890                     {
10891                       if (nodeDomains[oldId].size() >= 2) // a multiple node
10892                         {
10893                           vector<int> orderedDoms;
10894                           //MESSAGE("multiple node " << oldId);
10895                           if (mutipleNodes.count(oldId))
10896                             orderedDoms = mutipleNodes[oldId];
10897                           else
10898                             {
10899                               map<int,int>::iterator it = nodeDomains[oldId].begin();
10900                               for (; it != nodeDomains[oldId].end(); ++it)
10901                                 orderedDoms.push_back(it->first);
10902                             }
10903                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
10904                           //stringstream txt;
10905                           //for (int i=0; i<orderedDoms.size(); i++)
10906                           //  txt << orderedDoms[i] << " ";
10907                           //MESSAGE("orderedDoms " << txt.str());
10908                           mutipleNodes[oldId] = orderedDoms;
10909                         }
10910                       double *coords = grid->GetPoint(oldId);
10911                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10912                       copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
10913                       int newId = newNode->getVtkId();
10914                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
10915                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10916                     }
10917                 }
10918             }
10919         }
10920     }
10921
10922   MESSAGE(".. Creation of elements");
10923   for (int idomain = idom0; idomain < nbDomains; idomain++)
10924     {
10925       itface = faceDomains.begin();
10926       for (; itface != faceDomains.end(); ++itface)
10927         {
10928           std::map<int, int> domvol = itface->second;
10929           if (!domvol.count(idomain))
10930             continue;
10931           DownIdType face = itface->first;
10932           //MESSAGE(" --- face " << face.cellId);
10933           std::set<int> oldNodes;
10934           oldNodes.clear();
10935           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10936           int nbMultipleNodes = 0;
10937           std::set<int>::iterator itn = oldNodes.begin();
10938           for (; itn != oldNodes.end(); ++itn)
10939             {
10940               int oldId = *itn;
10941               if (mutipleNodes.count(oldId))
10942                 nbMultipleNodes++;
10943             }
10944           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10945             {
10946               //MESSAGE("multiple Nodes detected on a shared face");
10947               int downId = itface->first.cellId;
10948               unsigned char cellType = itface->first.cellType;
10949               // --- shared edge or shared face ?
10950               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10951                 {
10952                   int nodes[3];
10953                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10954                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10955                     if (mutipleNodes.count(nodes[i]))
10956                       if (!mutipleNodesToFace.count(nodes[i]))
10957                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10958                 }
10959               else // shared face (between two volumes)
10960                 {
10961                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10962                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10963                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10964                   for (int ie =0; ie < nbEdges; ie++)
10965                     {
10966                       int nodes[3];
10967                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10968                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10969                         {
10970                           vector<int> vn0 = mutipleNodes[nodes[0]];
10971                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10972                           vector<int> doms;
10973                           for (int i0 = 0; i0 < vn0.size(); i0++)
10974                             for (int i1 = 0; i1 < vn1.size(); i1++)
10975                               if (vn0[i0] == vn1[i1])
10976                                 doms.push_back(vn0[i0]);
10977                           if (doms.size() >2)
10978                             {
10979                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10980                               double *coords = grid->GetPoint(nodes[0]);
10981                               gp_Pnt p0(coords[0], coords[1], coords[2]);
10982                               coords = grid->GetPoint(nodes[nbNodes - 1]);
10983                               gp_Pnt p1(coords[0], coords[1], coords[2]);
10984                               gp_Pnt gref;
10985                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
10986                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10987                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10988                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10989                               for (int id=0; id < doms.size(); id++)
10990                                 {
10991                                   int idom = doms[id];
10992                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10993                                   for (int ivol=0; ivol<nbvol; ivol++)
10994                                     {
10995                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10996                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10997                                       if (domain.count(elem))
10998                                         {
10999                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11000                                           domvol[idom] = svol;
11001                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11002                                           double values[3];
11003                                           vtkIdType npts = 0;
11004                                           vtkIdType* pts = 0;
11005                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11006                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11007                                           if (id ==0)
11008                                             {
11009                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11010                                               angleDom[idom] = 0;
11011                                             }
11012                                           else
11013                                             {
11014                                               gp_Pnt g(values[0], values[1], values[2]);
11015                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11016                                               //MESSAGE("  angle=" << angleDom[idom]);
11017                                             }
11018                                           break;
11019                                         }
11020                                     }
11021                                 }
11022                               map<double, int> sortedDom; // sort domains by angle
11023                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11024                                 sortedDom[ia->second] = ia->first;
11025                               vector<int> vnodes;
11026                               vector<int> vdom;
11027                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11028                                 {
11029                                   vdom.push_back(ib->second);
11030                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11031                                 }
11032                               for (int ino = 0; ino < nbNodes; ino++)
11033                                 vnodes.push_back(nodes[ino]);
11034                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11035                             }
11036                         }
11037                     }
11038                 }
11039             }
11040         }
11041     }
11042
11043   // --- iterate on shared faces (volumes to modify, face to extrude)
11044   //     get node id's of the face (id SMDS = id VTK)
11045   //     create flat element with old and new nodes if requested
11046
11047   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11048   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11049
11050   std::map<int, std::map<long,int> > nodeQuadDomains;
11051   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11052
11053   MESSAGE(".. Creation of elements: simple junction");
11054   if (createJointElems)
11055     {
11056       int idg;
11057       string joints2DName = "joints2D";
11058       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11059       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11060       string joints3DName = "joints3D";
11061       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11062       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11063
11064       itface = faceDomains.begin();
11065       for (; itface != faceDomains.end(); ++itface)
11066         {
11067           DownIdType face = itface->first;
11068           std::set<int> oldNodes;
11069           std::set<int>::iterator itn;
11070           oldNodes.clear();
11071           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11072
11073           std::map<int, int> domvol = itface->second;
11074           std::map<int, int>::iterator itdom = domvol.begin();
11075           int dom1 = itdom->first;
11076           int vtkVolId = itdom->second;
11077           itdom++;
11078           int dom2 = itdom->first;
11079           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11080                                                              nodeQuadDomains);
11081           stringstream grpname;
11082           grpname << "j_";
11083           if (dom1 < dom2)
11084             grpname << dom1 << "_" << dom2;
11085           else
11086             grpname << dom2 << "_" << dom1;
11087           string namegrp = grpname.str();
11088           if (!mapOfJunctionGroups.count(namegrp))
11089             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11090           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11091           if (sgrp)
11092             sgrp->Add(vol->GetID());
11093           if (vol->GetType() == SMDSAbs_Volume)
11094             joints3DGrp->Add(vol->GetID());
11095           else if (vol->GetType() == SMDSAbs_Face)
11096             joints2DGrp->Add(vol->GetID());
11097         }
11098     }
11099
11100   // --- create volumes on multiple domain intersection if requested
11101   //     iterate on mutipleNodesToFace
11102   //     iterate on edgesMultiDomains
11103
11104   MESSAGE(".. Creation of elements: multiple junction");
11105   if (createJointElems)
11106     {
11107       // --- iterate on mutipleNodesToFace
11108
11109       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11110       for (; itn != mutipleNodesToFace.end(); ++itn)
11111         {
11112           int node = itn->first;
11113           vector<int> orderDom = itn->second;
11114           vector<vtkIdType> orderedNodes;
11115           for (int idom = 0; idom <orderDom.size(); idom++)
11116             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11117             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11118
11119             stringstream grpname;
11120             grpname << "m2j_";
11121             grpname << 0 << "_" << 0;
11122             int idg;
11123             string namegrp = grpname.str();
11124             if (!mapOfJunctionGroups.count(namegrp))
11125               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11126             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11127             if (sgrp)
11128               sgrp->Add(face->GetID());
11129         }
11130
11131       // --- iterate on edgesMultiDomains
11132
11133       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11134       for (; ite != edgesMultiDomains.end(); ++ite)
11135         {
11136           vector<int> nodes = ite->first;
11137           vector<int> orderDom = ite->second;
11138           vector<vtkIdType> orderedNodes;
11139           if (nodes.size() == 2)
11140             {
11141               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11142               for (int ino=0; ino < nodes.size(); ino++)
11143                 if (orderDom.size() == 3)
11144                   for (int idom = 0; idom <orderDom.size(); idom++)
11145                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11146                 else
11147                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11148                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11149               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11150
11151               int idg;
11152               string namegrp = "jointsMultiples";
11153               if (!mapOfJunctionGroups.count(namegrp))
11154                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11155               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11156               if (sgrp)
11157                 sgrp->Add(vol->GetID());
11158             }
11159           else
11160             {
11161               INFOS("Quadratic multiple joints not implemented");
11162               // TODO quadratic nodes
11163             }
11164         }
11165     }
11166
11167   // --- list the explicit faces and edges of the mesh that need to be modified,
11168   //     i.e. faces and edges built with one or more duplicated nodes.
11169   //     associate these faces or edges to their corresponding domain.
11170   //     only the first domain found is kept when a face or edge is shared
11171
11172   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11173   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11174   faceOrEdgeDom.clear();
11175   feDom.clear();
11176
11177   MESSAGE(".. Modification of elements");
11178   for (int idomain = idom0; idomain < nbDomains; idomain++)
11179     {
11180       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11181       for (; itnod != nodeDomains.end(); ++itnod)
11182         {
11183           int oldId = itnod->first;
11184           //MESSAGE("     node " << oldId);
11185           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11186           for (int i = 0; i < l.ncells; i++)
11187             {
11188               int vtkId = l.cells[i];
11189               int vtkType = grid->GetCellType(vtkId);
11190               int downId = grid->CellIdToDownId(vtkId);
11191               if (downId < 0)
11192                 continue; // new cells: not to be modified
11193               DownIdType aCell(downId, vtkType);
11194               int volParents[1000];
11195               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11196               for (int j = 0; j < nbvol; j++)
11197                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11198                   if (!feDom.count(vtkId))
11199                     {
11200                       feDom[vtkId] = idomain;
11201                       faceOrEdgeDom[aCell] = emptyMap;
11202                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11203                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11204                       //        << " type " << vtkType << " downId " << downId);
11205                     }
11206             }
11207         }
11208     }
11209
11210   // --- iterate on shared faces (volumes to modify, face to extrude)
11211   //     get node id's of the face
11212   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11213
11214   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11215   for (int m=0; m<3; m++)
11216     {
11217       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11218       itface = (*amap).begin();
11219       for (; itface != (*amap).end(); ++itface)
11220         {
11221           DownIdType face = itface->first;
11222           std::set<int> oldNodes;
11223           std::set<int>::iterator itn;
11224           oldNodes.clear();
11225           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11226           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11227           std::map<int, int> localClonedNodeIds;
11228
11229           std::map<int, int> domvol = itface->second;
11230           std::map<int, int>::iterator itdom = domvol.begin();
11231           for (; itdom != domvol.end(); ++itdom)
11232             {
11233               int idom = itdom->first;
11234               int vtkVolId = itdom->second;
11235               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11236               localClonedNodeIds.clear();
11237               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11238                 {
11239                   int oldId = *itn;
11240                   if (nodeDomains[oldId].count(idom))
11241                     {
11242                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11243                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11244                     }
11245                 }
11246               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11247             }
11248         }
11249     }
11250
11251   // Remove empty groups (issue 0022812)
11252   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11253   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11254   {
11255     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11256       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11257   }
11258
11259   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11260   grid->BuildLinks();
11261
11262   CHRONOSTOP(50);
11263   counters::stats();
11264   return true;
11265 }
11266
11267 /*!
11268  * \brief Double nodes on some external faces and create flat elements.
11269  * Flat elements are mainly used by some types of mechanic calculations.
11270  *
11271  * Each group of the list must be constituted of faces.
11272  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11273  * @param theElems - list of groups of faces, where a group of faces is a set of
11274  * SMDS_MeshElements sorted by Id.
11275  * @return TRUE if operation has been completed successfully, FALSE otherwise
11276  */
11277 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11278 {
11279   MESSAGE("-------------------------------------------------");
11280   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11281   MESSAGE("-------------------------------------------------");
11282
11283   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11284
11285   // --- For each group of faces
11286   //     duplicate the nodes, create a flat element based on the face
11287   //     replace the nodes of the faces by their clones
11288
11289   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11290   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11291   clonedNodes.clear();
11292   intermediateNodes.clear();
11293   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11294   mapOfJunctionGroups.clear();
11295
11296   for (int idom = 0; idom < theElems.size(); idom++)
11297     {
11298       const TIDSortedElemSet& domain = theElems[idom];
11299       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11300       for (; elemItr != domain.end(); ++elemItr)
11301         {
11302           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11303           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11304           if (!aFace)
11305             continue;
11306           // MESSAGE("aFace=" << aFace->GetID());
11307           bool isQuad = aFace->IsQuadratic();
11308           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11309
11310           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11311
11312           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11313           while (nodeIt->more())
11314             {
11315               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11316               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11317               if (isMedium)
11318                 ln2.push_back(node);
11319               else
11320                 ln0.push_back(node);
11321
11322               const SMDS_MeshNode* clone = 0;
11323               if (!clonedNodes.count(node))
11324                 {
11325                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11326                   copyPosition( node, clone );
11327                   clonedNodes[node] = clone;
11328                 }
11329               else
11330                 clone = clonedNodes[node];
11331
11332               if (isMedium)
11333                 ln3.push_back(clone);
11334               else
11335                 ln1.push_back(clone);
11336
11337               const SMDS_MeshNode* inter = 0;
11338               if (isQuad && (!isMedium))
11339                 {
11340                   if (!intermediateNodes.count(node))
11341                     {
11342                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11343                       copyPosition( node, inter );
11344                       intermediateNodes[node] = inter;
11345                     }
11346                   else
11347                     inter = intermediateNodes[node];
11348                   ln4.push_back(inter);
11349                 }
11350             }
11351
11352           // --- extrude the face
11353
11354           vector<const SMDS_MeshNode*> ln;
11355           SMDS_MeshVolume* vol = 0;
11356           vtkIdType aType = aFace->GetVtkType();
11357           switch (aType)
11358           {
11359             case VTK_TRIANGLE:
11360               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11361               // MESSAGE("vol prism " << vol->GetID());
11362               ln.push_back(ln1[0]);
11363               ln.push_back(ln1[1]);
11364               ln.push_back(ln1[2]);
11365               break;
11366             case VTK_QUAD:
11367               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11368               // MESSAGE("vol hexa " << vol->GetID());
11369               ln.push_back(ln1[0]);
11370               ln.push_back(ln1[1]);
11371               ln.push_back(ln1[2]);
11372               ln.push_back(ln1[3]);
11373               break;
11374             case VTK_QUADRATIC_TRIANGLE:
11375               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11376                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11377               // MESSAGE("vol quad prism " << vol->GetID());
11378               ln.push_back(ln1[0]);
11379               ln.push_back(ln1[1]);
11380               ln.push_back(ln1[2]);
11381               ln.push_back(ln3[0]);
11382               ln.push_back(ln3[1]);
11383               ln.push_back(ln3[2]);
11384               break;
11385             case VTK_QUADRATIC_QUAD:
11386 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11387 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11388 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11389               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11390                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11391                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11392               // MESSAGE("vol quad hexa " << vol->GetID());
11393               ln.push_back(ln1[0]);
11394               ln.push_back(ln1[1]);
11395               ln.push_back(ln1[2]);
11396               ln.push_back(ln1[3]);
11397               ln.push_back(ln3[0]);
11398               ln.push_back(ln3[1]);
11399               ln.push_back(ln3[2]);
11400               ln.push_back(ln3[3]);
11401               break;
11402             case VTK_POLYGON:
11403               break;
11404             default:
11405               break;
11406           }
11407
11408           if (vol)
11409             {
11410               stringstream grpname;
11411               grpname << "jf_";
11412               grpname << idom;
11413               int idg;
11414               string namegrp = grpname.str();
11415               if (!mapOfJunctionGroups.count(namegrp))
11416                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11417               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11418               if (sgrp)
11419                 sgrp->Add(vol->GetID());
11420             }
11421
11422           // --- modify the face
11423
11424           aFace->ChangeNodes(&ln[0], ln.size());
11425         }
11426     }
11427   return true;
11428 }
11429
11430 /*!
11431  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11432  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11433  *  groups of faces to remove inside the object, (idem edges).
11434  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11435  */
11436 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11437                                       const TopoDS_Shape& theShape,
11438                                       SMESH_NodeSearcher* theNodeSearcher,
11439                                       const char* groupName,
11440                                       std::vector<double>&   nodesCoords,
11441                                       std::vector<std::vector<int> >& listOfListOfNodes)
11442 {
11443   MESSAGE("--------------------------------");
11444   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11445   MESSAGE("--------------------------------");
11446
11447   // --- zone of volumes to remove is given :
11448   //     1 either by a geom shape (one or more vertices) and a radius,
11449   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11450   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11451   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11452   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11453   //     defined by it's name.
11454
11455   SMESHDS_GroupBase* groupDS = 0;
11456   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11457   while ( groupIt->more() )
11458     {
11459       groupDS = 0;
11460       SMESH_Group * group = groupIt->next();
11461       if ( !group ) continue;
11462       groupDS = group->GetGroupDS();
11463       if ( !groupDS || groupDS->IsEmpty() ) continue;
11464       std::string grpName = group->GetName();
11465       //MESSAGE("grpName=" << grpName);
11466       if (grpName == groupName)
11467         break;
11468       else
11469         groupDS = 0;
11470     }
11471
11472   bool isNodeGroup = false;
11473   bool isNodeCoords = false;
11474   if (groupDS)
11475     {
11476       if (groupDS->GetType() != SMDSAbs_Node)
11477         return;
11478       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11479     }
11480
11481   if (nodesCoords.size() > 0)
11482     isNodeCoords = true; // a list o nodes given by their coordinates
11483   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11484
11485   // --- define groups to build
11486
11487   int idg; // --- group of SMDS volumes
11488   string grpvName = groupName;
11489   grpvName += "_vol";
11490   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11491   if (!grp)
11492     {
11493       MESSAGE("group not created " << grpvName);
11494       return;
11495     }
11496   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11497
11498   int idgs; // --- group of SMDS faces on the skin
11499   string grpsName = groupName;
11500   grpsName += "_skin";
11501   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11502   if (!grps)
11503     {
11504       MESSAGE("group not created " << grpsName);
11505       return;
11506     }
11507   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11508
11509   int idgi; // --- group of SMDS faces internal (several shapes)
11510   string grpiName = groupName;
11511   grpiName += "_internalFaces";
11512   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11513   if (!grpi)
11514     {
11515       MESSAGE("group not created " << grpiName);
11516       return;
11517     }
11518   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11519
11520   int idgei; // --- group of SMDS faces internal (several shapes)
11521   string grpeiName = groupName;
11522   grpeiName += "_internalEdges";
11523   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11524   if (!grpei)
11525     {
11526       MESSAGE("group not created " << grpeiName);
11527       return;
11528     }
11529   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11530
11531   // --- build downward connectivity
11532
11533   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11534   meshDS->BuildDownWardConnectivity(true);
11535   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11536
11537   // --- set of volumes detected inside
11538
11539   std::set<int> setOfInsideVol;
11540   std::set<int> setOfVolToCheck;
11541
11542   std::vector<gp_Pnt> gpnts;
11543   gpnts.clear();
11544
11545   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11546     {
11547       MESSAGE("group of nodes provided");
11548       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11549       while ( elemIt->more() )
11550         {
11551           const SMDS_MeshElement* elem = elemIt->next();
11552           if (!elem)
11553             continue;
11554           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11555           if (!node)
11556             continue;
11557           SMDS_MeshElement* vol = 0;
11558           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11559           while (volItr->more())
11560             {
11561               vol = (SMDS_MeshElement*)volItr->next();
11562               setOfInsideVol.insert(vol->getVtkId());
11563               sgrp->Add(vol->GetID());
11564             }
11565         }
11566     }
11567   else if (isNodeCoords)
11568     {
11569       MESSAGE("list of nodes coordinates provided");
11570       int i = 0;
11571       int k = 0;
11572       while (i < nodesCoords.size()-2)
11573         {
11574           double x = nodesCoords[i++];
11575           double y = nodesCoords[i++];
11576           double z = nodesCoords[i++];
11577           gp_Pnt p = gp_Pnt(x, y ,z);
11578           gpnts.push_back(p);
11579           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11580           k++;
11581         }
11582     }
11583   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11584     {
11585       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11586       TopTools_IndexedMapOfShape vertexMap;
11587       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11588       gp_Pnt p = gp_Pnt(0,0,0);
11589       if (vertexMap.Extent() < 1)
11590         return;
11591
11592       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11593         {
11594           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11595           p = BRep_Tool::Pnt(vertex);
11596           gpnts.push_back(p);
11597           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11598         }
11599     }
11600
11601   if (gpnts.size() > 0)
11602     {
11603       int nodeId = 0;
11604       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11605       if (startNode)
11606         nodeId = startNode->GetID();
11607       MESSAGE("nodeId " << nodeId);
11608
11609       double radius2 = radius*radius;
11610       MESSAGE("radius2 " << radius2);
11611
11612       // --- volumes on start node
11613
11614       setOfVolToCheck.clear();
11615       SMDS_MeshElement* startVol = 0;
11616       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11617       while (volItr->more())
11618         {
11619           startVol = (SMDS_MeshElement*)volItr->next();
11620           setOfVolToCheck.insert(startVol->getVtkId());
11621         }
11622       if (setOfVolToCheck.empty())
11623         {
11624           MESSAGE("No volumes found");
11625           return;
11626         }
11627
11628       // --- starting with central volumes then their neighbors, check if they are inside
11629       //     or outside the domain, until no more new neighbor volume is inside.
11630       //     Fill the group of inside volumes
11631
11632       std::map<int, double> mapOfNodeDistance2;
11633       mapOfNodeDistance2.clear();
11634       std::set<int> setOfOutsideVol;
11635       while (!setOfVolToCheck.empty())
11636         {
11637           std::set<int>::iterator it = setOfVolToCheck.begin();
11638           int vtkId = *it;
11639           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11640           bool volInside = false;
11641           vtkIdType npts = 0;
11642           vtkIdType* pts = 0;
11643           grid->GetCellPoints(vtkId, npts, pts);
11644           for (int i=0; i<npts; i++)
11645             {
11646               double distance2 = 0;
11647               if (mapOfNodeDistance2.count(pts[i]))
11648                 {
11649                   distance2 = mapOfNodeDistance2[pts[i]];
11650                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11651                 }
11652               else
11653                 {
11654                   double *coords = grid->GetPoint(pts[i]);
11655                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11656                   distance2 = 1.E40;
11657                   for (int j=0; j<gpnts.size(); j++)
11658                     {
11659                       double d2 = aPoint.SquareDistance(gpnts[j]);
11660                       if (d2 < distance2)
11661                         {
11662                           distance2 = d2;
11663                           if (distance2 < radius2)
11664                             break;
11665                         }
11666                     }
11667                   mapOfNodeDistance2[pts[i]] = distance2;
11668                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11669                 }
11670               if (distance2 < radius2)
11671                 {
11672                   volInside = true; // one or more nodes inside the domain
11673                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11674                   break;
11675                 }
11676             }
11677           if (volInside)
11678             {
11679               setOfInsideVol.insert(vtkId);
11680               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11681               int neighborsVtkIds[NBMAXNEIGHBORS];
11682               int downIds[NBMAXNEIGHBORS];
11683               unsigned char downTypes[NBMAXNEIGHBORS];
11684               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11685               for (int n = 0; n < nbNeighbors; n++)
11686                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11687                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11688             }
11689           else
11690             {
11691               setOfOutsideVol.insert(vtkId);
11692               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11693             }
11694           setOfVolToCheck.erase(vtkId);
11695         }
11696     }
11697
11698   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11699   //     If yes, add the volume to the inside set
11700
11701   bool addedInside = true;
11702   std::set<int> setOfVolToReCheck;
11703   while (addedInside)
11704     {
11705       MESSAGE(" --------------------------- re check");
11706       addedInside = false;
11707       std::set<int>::iterator itv = setOfInsideVol.begin();
11708       for (; itv != setOfInsideVol.end(); ++itv)
11709         {
11710           int vtkId = *itv;
11711           int neighborsVtkIds[NBMAXNEIGHBORS];
11712           int downIds[NBMAXNEIGHBORS];
11713           unsigned char downTypes[NBMAXNEIGHBORS];
11714           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11715           for (int n = 0; n < nbNeighbors; n++)
11716             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11717               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11718         }
11719       setOfVolToCheck = setOfVolToReCheck;
11720       setOfVolToReCheck.clear();
11721       while  (!setOfVolToCheck.empty())
11722         {
11723           std::set<int>::iterator it = setOfVolToCheck.begin();
11724           int vtkId = *it;
11725           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11726             {
11727               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11728               int countInside = 0;
11729               int neighborsVtkIds[NBMAXNEIGHBORS];
11730               int downIds[NBMAXNEIGHBORS];
11731               unsigned char downTypes[NBMAXNEIGHBORS];
11732               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11733               for (int n = 0; n < nbNeighbors; n++)
11734                 if (setOfInsideVol.count(neighborsVtkIds[n]))
11735                   countInside++;
11736               MESSAGE("countInside " << countInside);
11737               if (countInside > 1)
11738                 {
11739                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11740                   setOfInsideVol.insert(vtkId);
11741                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11742                   addedInside = true;
11743                 }
11744               else
11745                 setOfVolToReCheck.insert(vtkId);
11746             }
11747           setOfVolToCheck.erase(vtkId);
11748         }
11749     }
11750
11751   // --- map of Downward faces at the boundary, inside the global volume
11752   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11753   //     fill group of SMDS faces inside the volume (when several volume shapes)
11754   //     fill group of SMDS faces on the skin of the global volume (if skin)
11755
11756   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11757   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
11758   std::set<int>::iterator it = setOfInsideVol.begin();
11759   for (; it != setOfInsideVol.end(); ++it)
11760     {
11761       int vtkId = *it;
11762       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11763       int neighborsVtkIds[NBMAXNEIGHBORS];
11764       int downIds[NBMAXNEIGHBORS];
11765       unsigned char downTypes[NBMAXNEIGHBORS];
11766       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11767       for (int n = 0; n < nbNeighbors; n++)
11768         {
11769           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11770           if (neighborDim == 3)
11771             {
11772               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11773                 {
11774                   DownIdType face(downIds[n], downTypes[n]);
11775                   boundaryFaces[face] = vtkId;
11776                 }
11777               // if the face between to volumes is in the mesh, get it (internal face between shapes)
11778               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11779               if (vtkFaceId >= 0)
11780                 {
11781                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11782                   // find also the smds edges on this face
11783                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11784                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11785                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11786                   for (int i = 0; i < nbEdges; i++)
11787                     {
11788                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11789                       if (vtkEdgeId >= 0)
11790                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11791                     }
11792                 }
11793             }
11794           else if (neighborDim == 2) // skin of the volume
11795             {
11796               DownIdType face(downIds[n], downTypes[n]);
11797               skinFaces[face] = vtkId;
11798               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11799               if (vtkFaceId >= 0)
11800                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11801             }
11802         }
11803     }
11804
11805   // --- identify the edges constituting the wire of each subshape on the skin
11806   //     define polylines with the nodes of edges, equivalent to wires
11807   //     project polylines on subshapes, and partition, to get geom faces
11808
11809   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11810   std::set<int> emptySet;
11811   emptySet.clear();
11812   std::set<int> shapeIds;
11813
11814   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11815   while (itelem->more())
11816     {
11817       const SMDS_MeshElement *elem = itelem->next();
11818       int shapeId = elem->getshapeId();
11819       int vtkId = elem->getVtkId();
11820       if (!shapeIdToVtkIdSet.count(shapeId))
11821         {
11822           shapeIdToVtkIdSet[shapeId] = emptySet;
11823           shapeIds.insert(shapeId);
11824         }
11825       shapeIdToVtkIdSet[shapeId].insert(vtkId);
11826     }
11827
11828   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11829   std::set<DownIdType, DownIdCompare> emptyEdges;
11830   emptyEdges.clear();
11831
11832   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
11833   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11834     {
11835       int shapeId = itShape->first;
11836       MESSAGE(" --- Shape ID --- "<< shapeId);
11837       shapeIdToEdges[shapeId] = emptyEdges;
11838
11839       std::vector<int> nodesEdges;
11840
11841       std::set<int>::iterator its = itShape->second.begin();
11842       for (; its != itShape->second.end(); ++its)
11843         {
11844           int vtkId = *its;
11845           MESSAGE("     " << vtkId);
11846           int neighborsVtkIds[NBMAXNEIGHBORS];
11847           int downIds[NBMAXNEIGHBORS];
11848           unsigned char downTypes[NBMAXNEIGHBORS];
11849           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11850           for (int n = 0; n < nbNeighbors; n++)
11851             {
11852               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11853                 continue;
11854               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11855               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11856               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11857                 {
11858                   DownIdType edge(downIds[n], downTypes[n]);
11859                   if (!shapeIdToEdges[shapeId].count(edge))
11860                     {
11861                       shapeIdToEdges[shapeId].insert(edge);
11862                       int vtkNodeId[3];
11863                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11864                       nodesEdges.push_back(vtkNodeId[0]);
11865                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11866                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11867                     }
11868                 }
11869             }
11870         }
11871
11872       std::list<int> order;
11873       order.clear();
11874       if (nodesEdges.size() > 0)
11875         {
11876           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
11877           nodesEdges[0] = -1;
11878           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
11879           nodesEdges[1] = -1; // do not reuse this edge
11880           bool found = true;
11881           while (found)
11882             {
11883               int nodeTofind = order.back(); // try first to push back
11884               int i = 0;
11885               for (i = 0; i<nodesEdges.size(); i++)
11886                 if (nodesEdges[i] == nodeTofind)
11887                   break;
11888               if (i == nodesEdges.size())
11889                 found = false; // no follower found on back
11890               else
11891                 {
11892                   if (i%2) // odd ==> use the previous one
11893                     if (nodesEdges[i-1] < 0)
11894                       found = false;
11895                     else
11896                       {
11897                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
11898                         nodesEdges[i-1] = -1;
11899                       }
11900                   else // even ==> use the next one
11901                     if (nodesEdges[i+1] < 0)
11902                       found = false;
11903                     else
11904                       {
11905                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
11906                         nodesEdges[i+1] = -1;
11907                       }
11908                 }
11909               if (found)
11910                 continue;
11911               // try to push front
11912               found = true;
11913               nodeTofind = order.front(); // try to push front
11914               for (i = 0; i<nodesEdges.size(); i++)
11915                 if (nodesEdges[i] == nodeTofind)
11916                   break;
11917               if (i == nodesEdges.size())
11918                 {
11919                   found = false; // no predecessor found on front
11920                   continue;
11921                 }
11922               if (i%2) // odd ==> use the previous one
11923                 if (nodesEdges[i-1] < 0)
11924                   found = false;
11925                 else
11926                   {
11927                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
11928                     nodesEdges[i-1] = -1;
11929                   }
11930               else // even ==> use the next one
11931                 if (nodesEdges[i+1] < 0)
11932                   found = false;
11933                 else
11934                   {
11935                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
11936                     nodesEdges[i+1] = -1;
11937                   }
11938             }
11939         }
11940
11941
11942       std::vector<int> nodes;
11943       nodes.push_back(shapeId);
11944       std::list<int>::iterator itl = order.begin();
11945       for (; itl != order.end(); itl++)
11946         {
11947           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11948           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
11949         }
11950       listOfListOfNodes.push_back(nodes);
11951     }
11952
11953   //     partition geom faces with blocFissure
11954   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11955   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11956
11957   return;
11958 }
11959
11960
11961 //================================================================================
11962 /*!
11963  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11964  * The created 2D mesh elements based on nodes of free faces of boundary volumes
11965  * \return TRUE if operation has been completed successfully, FALSE otherwise
11966  */
11967 //================================================================================
11968
11969 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11970 {
11971   // iterates on volume elements and detect all free faces on them
11972   SMESHDS_Mesh* aMesh = GetMeshDS();
11973   if (!aMesh)
11974     return false;
11975   //bool res = false;
11976   int nbFree = 0, nbExisted = 0, nbCreated = 0;
11977   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11978   while(vIt->more())
11979   {
11980     const SMDS_MeshVolume* volume = vIt->next();
11981     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11982     vTool.SetExternalNormal();
11983     //const bool isPoly = volume->IsPoly();
11984     const int iQuad = volume->IsQuadratic();
11985     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11986     {
11987       if (!vTool.IsFreeFace(iface))
11988         continue;
11989       nbFree++;
11990       vector<const SMDS_MeshNode *> nodes;
11991       int nbFaceNodes = vTool.NbFaceNodes(iface);
11992       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11993       int inode = 0;
11994       for ( ; inode < nbFaceNodes; inode += iQuad+1)
11995         nodes.push_back(faceNodes[inode]);
11996       if (iQuad) { // add medium nodes
11997         for ( inode = 1; inode < nbFaceNodes; inode += 2)
11998           nodes.push_back(faceNodes[inode]);
11999         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12000           nodes.push_back(faceNodes[8]);
12001       }
12002       // add new face based on volume nodes
12003       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12004         nbExisted++;
12005         continue; // face already exsist
12006       }
12007       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12008       nbCreated++;
12009     }
12010   }
12011   return ( nbFree==(nbExisted+nbCreated) );
12012 }
12013
12014 namespace
12015 {
12016   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12017   {
12018     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12019       return n;
12020     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12021   }
12022 }
12023 //================================================================================
12024 /*!
12025  * \brief Creates missing boundary elements
12026  *  \param elements - elements whose boundary is to be checked
12027  *  \param dimension - defines type of boundary elements to create
12028  *  \param group - a group to store created boundary elements in
12029  *  \param targetMesh - a mesh to store created boundary elements in
12030  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12031  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12032  *                                boundary elements will be copied into the targetMesh
12033  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12034  *                                boundary elements will be added into the new group
12035  *  \param aroundElements - if true, elements will be created on boundary of given
12036  *                          elements else, on boundary of the whole mesh.
12037  * \return nb of added boundary elements
12038  */
12039 //================================================================================
12040
12041 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12042                                        Bnd_Dimension           dimension,
12043                                        SMESH_Group*            group/*=0*/,
12044                                        SMESH_Mesh*             targetMesh/*=0*/,
12045                                        bool                    toCopyElements/*=false*/,
12046                                        bool                    toCopyExistingBoundary/*=false*/,
12047                                        bool                    toAddExistingBondary/*= false*/,
12048                                        bool                    aroundElements/*= false*/)
12049 {
12050   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12051   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12052   // hope that all elements are of the same type, do not check them all
12053   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12054     throw SALOME_Exception(LOCALIZED("wrong element type"));
12055
12056   if ( !targetMesh )
12057     toCopyElements = toCopyExistingBoundary = false;
12058
12059   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12060   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12061   int nbAddedBnd = 0;
12062
12063   // editor adding present bnd elements and optionally holding elements to add to the group
12064   SMESH_MeshEditor* presentEditor;
12065   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12066   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12067
12068   SMESH_MesherHelper helper( *myMesh );
12069   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12070   SMDS_VolumeTool vTool;
12071   TIDSortedElemSet avoidSet;
12072   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12073   int inode;
12074
12075   typedef vector<const SMDS_MeshNode*> TConnectivity;
12076
12077   SMDS_ElemIteratorPtr eIt;
12078   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12079   else                  eIt = elemSetIterator( elements );
12080
12081   while (eIt->more())
12082   {
12083     const SMDS_MeshElement* elem = eIt->next();
12084     const int              iQuad = elem->IsQuadratic();
12085
12086     // ------------------------------------------------------------------------------------
12087     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12088     // ------------------------------------------------------------------------------------
12089     vector<const SMDS_MeshElement*> presentBndElems;
12090     vector<TConnectivity>           missingBndElems;
12091     TConnectivity nodes, elemNodes;
12092     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12093     {
12094       vTool.SetExternalNormal();
12095       const SMDS_MeshElement* otherVol = 0;
12096       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12097       {
12098         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12099              ( !aroundElements || elements.count( otherVol )))
12100           continue;
12101         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12102         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12103         if ( missType == SMDSAbs_Edge ) // boundary edges
12104         {
12105           nodes.resize( 2+iQuad );
12106           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12107           {
12108             for ( int j = 0; j < nodes.size(); ++j )
12109               nodes[j] =nn[i+j];
12110             if ( const SMDS_MeshElement* edge =
12111                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12112               presentBndElems.push_back( edge );
12113             else
12114               missingBndElems.push_back( nodes );
12115           }
12116         }
12117         else // boundary face
12118         {
12119           nodes.clear();
12120           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12121             nodes.push_back( nn[inode] ); // add corner nodes
12122           if (iQuad)
12123             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12124               nodes.push_back( nn[inode] ); // add medium nodes
12125           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12126           if ( iCenter > 0 )
12127             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12128
12129           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12130                                                                SMDSAbs_Face, /*noMedium=*/false ))
12131             presentBndElems.push_back( f );
12132           else
12133             missingBndElems.push_back( nodes );
12134
12135           if ( targetMesh != myMesh )
12136           {
12137             // add 1D elements on face boundary to be added to a new mesh
12138             const SMDS_MeshElement* edge;
12139             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12140             {
12141               if ( iQuad )
12142                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12143               else
12144                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12145               if ( edge && avoidSet.insert( edge ).second )
12146                 presentBndElems.push_back( edge );
12147             }
12148           }
12149         }
12150       }
12151     }
12152     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12153     {
12154       avoidSet.clear(), avoidSet.insert( elem );
12155       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12156                         SMDS_MeshElement::iterator() );
12157       elemNodes.push_back( elemNodes[0] );
12158       nodes.resize( 2 + iQuad );
12159       const int nbLinks = elem->NbCornerNodes();
12160       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12161       {
12162         nodes[0] = elemNodes[iN];
12163         nodes[1] = elemNodes[iN+1+iQuad];
12164         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12165           continue; // not free link
12166
12167         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12168         if ( const SMDS_MeshElement* edge =
12169              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12170           presentBndElems.push_back( edge );
12171         else
12172           missingBndElems.push_back( nodes );
12173       }
12174     }
12175
12176     // ---------------------------------
12177     // 2. Add missing boundary elements
12178     // ---------------------------------
12179     if ( targetMesh != myMesh )
12180       // instead of making a map of nodes in this mesh and targetMesh,
12181       // we create nodes with same IDs.
12182       for ( int i = 0; i < missingBndElems.size(); ++i )
12183       {
12184         TConnectivity& srcNodes = missingBndElems[i];
12185         TConnectivity  nodes( srcNodes.size() );
12186         for ( inode = 0; inode < nodes.size(); ++inode )
12187           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12188         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12189                                                                    missType,
12190                                                                    /*noMedium=*/false))
12191           continue;
12192         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12193         ++nbAddedBnd;
12194       }
12195     else
12196       for ( int i = 0; i < missingBndElems.size(); ++i )
12197       {
12198         TConnectivity& nodes = missingBndElems[i];
12199         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12200                                                                    missType,
12201                                                                    /*noMedium=*/false))
12202           continue;
12203         SMDS_MeshElement* elem =
12204           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12205         ++nbAddedBnd;
12206
12207         // try to set a new element to a shape
12208         if ( myMesh->HasShapeToMesh() )
12209         {
12210           bool ok = true;
12211           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12212           const int nbN = nodes.size() / (iQuad+1 );
12213           for ( inode = 0; inode < nbN && ok; ++inode )
12214           {
12215             pair<int, TopAbs_ShapeEnum> i_stype =
12216               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12217             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12218               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12219           }
12220           if ( ok && mediumShapes.size() > 1 )
12221           {
12222             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12223             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12224             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12225             {
12226               if (( ok = ( stype_i->first != stype_i_0.first )))
12227                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12228                                         aMesh->IndexToShape( stype_i_0.second ));
12229             }
12230           }
12231           if ( ok && mediumShapes.begin()->first == missShapeType )
12232             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12233         }
12234       }
12235
12236     // ----------------------------------
12237     // 3. Copy present boundary elements
12238     // ----------------------------------
12239     if ( toCopyExistingBoundary )
12240       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12241       {
12242         const SMDS_MeshElement* e = presentBndElems[i];
12243         TConnectivity nodes( e->NbNodes() );
12244         for ( inode = 0; inode < nodes.size(); ++inode )
12245           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12246         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12247       }
12248     else // store present elements to add them to a group
12249       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12250       {
12251         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12252       }
12253
12254   } // loop on given elements
12255
12256   // ---------------------------------------------
12257   // 4. Fill group with boundary elements
12258   // ---------------------------------------------
12259   if ( group )
12260   {
12261     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12262       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12263         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12264   }
12265   tgtEditor.myLastCreatedElems.Clear();
12266   tgtEditor2.myLastCreatedElems.Clear();
12267
12268   // -----------------------
12269   // 5. Copy given elements
12270   // -----------------------
12271   if ( toCopyElements && targetMesh != myMesh )
12272   {
12273     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12274     else                  eIt = elemSetIterator( elements );
12275     while (eIt->more())
12276     {
12277       const SMDS_MeshElement* elem = eIt->next();
12278       TConnectivity nodes( elem->NbNodes() );
12279       for ( inode = 0; inode < nodes.size(); ++inode )
12280         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12281       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12282
12283       tgtEditor.myLastCreatedElems.Clear();
12284     }
12285   }
12286   return nbAddedBnd;
12287 }
12288
12289 //================================================================================
12290 /*!
12291  * \brief Copy node position and set \a to node on the same geometry
12292  */
12293 //================================================================================
12294
12295 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12296                                      const SMDS_MeshNode* to )
12297 {
12298   if ( !from || !to ) return;
12299
12300   SMDS_PositionPtr pos = from->GetPosition();
12301   if ( !pos || from->getshapeId() < 1 ) return;
12302
12303   switch ( pos->GetTypeOfPosition() )
12304   {
12305   case SMDS_TOP_3DSPACE: break;
12306
12307   case SMDS_TOP_FACE:
12308   {
12309     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12310     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12311                                 fPos->GetUParameter(), fPos->GetVParameter() );
12312     break;
12313   }
12314   case SMDS_TOP_EDGE:
12315   {
12316     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12317     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12318     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12319     break;
12320   }
12321   case SMDS_TOP_VERTEX:
12322   {
12323     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12324     break;
12325   }
12326   case SMDS_TOP_UNSPEC:
12327   default:;
12328   }
12329 }