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