Salome HOME
b3401c601fc5c2fd06f21761a7852e98b3a43223
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2015  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 #include "chrono.hxx"
54
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
59 #include <ElCLib.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
70 #include <TopExp.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
78 #include <gp.hxx>
79 #include <gp_Ax1.hxx>
80 #include <gp_Dir.hxx>
81 #include <gp_Lin.hxx>
82 #include <gp_Pln.hxx>
83 #include <gp_Trsf.hxx>
84 #include <gp_Vec.hxx>
85 #include <gp_XY.hxx>
86 #include <gp_XYZ.hxx>
87
88 #include <cmath>
89
90 #include <map>
91 #include <set>
92 #include <numeric>
93 #include <limits>
94 #include <algorithm>
95 #include <sstream>
96
97 #include <boost/tuple/tuple.hpp>
98
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
101
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103
104 using namespace std;
105 using namespace SMESH::Controls;
106
107 namespace
108 {
109   template < class ELEM_SET >
110   SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111   {
112     typedef SMDS_SetIterator
113       < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114     return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115   }
116 }
117
118 //=======================================================================
119 //function : SMESH_MeshEditor
120 //purpose  :
121 //=======================================================================
122
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124   :myMesh( theMesh ) // theMesh may be NULL
125 {
126 }
127
128 //================================================================================
129 /*!
130  * \brief Clears myLastCreatedNodes and myLastCreatedElems
131  */
132 //================================================================================
133
134 void SMESH_MeshEditor::ClearLastCreated()
135 {
136   myLastCreatedNodes.Clear();
137   myLastCreatedElems.Clear();
138 }
139
140
141 //=======================================================================
142 /*!
143  * \brief Add element
144  */
145 //=======================================================================
146
147 SMDS_MeshElement*
148 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
149                              const SMDSAbs_ElementType            type,
150                              const bool                           isPoly,
151                              const int                            ID,
152                              const double                         ballDiameter)
153 {
154   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
155   SMDS_MeshElement* e = 0;
156   int nbnode = node.size();
157   SMESHDS_Mesh* mesh = GetMeshDS();
158   switch ( type ) {
159   case SMDSAbs_Face:
160     if ( !isPoly ) {
161       if      (nbnode == 3) {
162         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
163         else           e = mesh->AddFace      (node[0], node[1], node[2] );
164       }
165       else if (nbnode == 4) {
166         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
167         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
168       }
169       else if (nbnode == 6) {
170         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
171                                                node[4], node[5], ID);
172         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
173                                                node[4], node[5] );
174       }
175       else if (nbnode == 7) {
176         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
177                                                node[4], node[5], node[6], ID);
178         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
179                                                node[4], node[5], node[6] );
180       }
181       else if (nbnode == 8) {
182         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
183                                                node[4], node[5], node[6], node[7], ID);
184         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
185                                                node[4], node[5], node[6], node[7] );
186       }
187       else if (nbnode == 9) {
188         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
189                                                node[4], node[5], node[6], node[7], node[8], ID);
190         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
191                                                node[4], node[5], node[6], node[7], node[8] );
192       }
193     } else {
194       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
195       else           e = mesh->AddPolygonalFace      (node    );
196     }
197     break;
198
199   case SMDSAbs_Volume:
200     if ( !isPoly ) {
201       if      (nbnode == 4) {
202         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
203         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
204       }
205       else if (nbnode == 5) {
206         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
207                                                  node[4], ID);
208         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
209                                                  node[4] );
210       }
211       else if (nbnode == 6) {
212         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213                                                  node[4], node[5], ID);
214         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
215                                                  node[4], node[5] );
216       }
217       else if (nbnode == 8) {
218         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
219                                                  node[4], node[5], node[6], node[7], ID);
220         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
221                                                  node[4], node[5], node[6], node[7] );
222       }
223       else if (nbnode == 10) {
224         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
225                                                  node[4], node[5], node[6], node[7],
226                                                  node[8], node[9], ID);
227         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
228                                                  node[4], node[5], node[6], node[7],
229                                                  node[8], node[9] );
230       }
231       else if (nbnode == 12) {
232         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
233                                                  node[4], node[5], node[6], node[7],
234                                                  node[8], node[9], node[10], node[11], ID);
235         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
236                                                  node[4], node[5], node[6], node[7],
237                                                  node[8], node[9], node[10], node[11] );
238       }
239       else if (nbnode == 13) {
240         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
241                                                  node[4], node[5], node[6], node[7],
242                                                  node[8], node[9], node[10],node[11],
243                                                  node[12],ID);
244         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
245                                                  node[4], node[5], node[6], node[7],
246                                                  node[8], node[9], node[10],node[11],
247                                                  node[12] );
248       }
249       else if (nbnode == 15) {
250         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
251                                                  node[4], node[5], node[6], node[7],
252                                                  node[8], node[9], node[10],node[11],
253                                                  node[12],node[13],node[14],ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], node[6], node[7],
256                                                  node[8], node[9], node[10],node[11],
257                                                  node[12],node[13],node[14] );
258       }
259       else if (nbnode == 20) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7],
262                                                  node[8], node[9], node[10],node[11],
263                                                  node[12],node[13],node[14],node[15],
264                                                  node[16],node[17],node[18],node[19],ID);
265         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
266                                                  node[4], node[5], node[6], node[7],
267                                                  node[8], node[9], node[10],node[11],
268                                                  node[12],node[13],node[14],node[15],
269                                                  node[16],node[17],node[18],node[19] );
270       }
271       else if (nbnode == 27) {
272         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7],
274                                                  node[8], node[9], node[10],node[11],
275                                                  node[12],node[13],node[14],node[15],
276                                                  node[16],node[17],node[18],node[19],
277                                                  node[20],node[21],node[22],node[23],
278                                                  node[24],node[25],node[26], ID);
279         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
280                                                  node[4], node[5], node[6], node[7],
281                                                  node[8], node[9], node[10],node[11],
282                                                  node[12],node[13],node[14],node[15],
283                                                  node[16],node[17],node[18],node[19],
284                                                  node[20],node[21],node[22],node[23],
285                                                  node[24],node[25],node[26] );
286       }
287     }
288     break;
289
290   case SMDSAbs_Edge:
291     if ( nbnode == 2 ) {
292       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
293       else           e = mesh->AddEdge      (node[0], node[1] );
294     }
295     else if ( nbnode == 3 ) {
296       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
297       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
298     }
299     break;
300
301   case SMDSAbs_0DElement:
302     if ( nbnode == 1 ) {
303       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
304       else           e = mesh->Add0DElement      (node[0] );
305     }
306     break;
307
308   case SMDSAbs_Node:
309     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
310     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
311     break;
312
313   case SMDSAbs_Ball:
314     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
315     else           e = mesh->AddBall      (node[0], ballDiameter);
316     break;
317
318   default:;
319   }
320   if ( e ) myLastCreatedElems.Append( e );
321   return e;
322 }
323
324 //=======================================================================
325 /*!
326  * \brief Add element
327  */
328 //=======================================================================
329
330 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
331                                                const SMDSAbs_ElementType type,
332                                                const bool                isPoly,
333                                                const int                 ID)
334 {
335   vector<const SMDS_MeshNode*> nodes;
336   nodes.reserve( nodeIDs.size() );
337   vector<int>::const_iterator id = nodeIDs.begin();
338   while ( id != nodeIDs.end() ) {
339     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
340       nodes.push_back( node );
341     else
342       return 0;
343   }
344   return AddElement( nodes, type, isPoly, ID );
345 }
346
347 //=======================================================================
348 //function : Remove
349 //purpose  : Remove a node or an element.
350 //           Modify a compute state of sub-meshes which become empty
351 //=======================================================================
352
353 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
354                               const bool         isNodes )
355 {
356   myLastCreatedElems.Clear();
357   myLastCreatedNodes.Clear();
358
359   SMESHDS_Mesh* aMesh = GetMeshDS();
360   set< SMESH_subMesh *> smmap;
361
362   int removed = 0;
363   list<int>::const_iterator it = theIDs.begin();
364   for ( ; it != theIDs.end(); it++ ) {
365     const SMDS_MeshElement * elem;
366     if ( isNodes )
367       elem = aMesh->FindNode( *it );
368     else
369       elem = aMesh->FindElement( *it );
370     if ( !elem )
371       continue;
372
373     // Notify VERTEX sub-meshes about modification
374     if ( isNodes ) {
375       const SMDS_MeshNode* node = cast2Node( elem );
376       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
377         if ( int aShapeID = node->getshapeId() )
378           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
379             smmap.insert( sm );
380     }
381     // Find sub-meshes to notify about modification
382     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
383     //     while ( nodeIt->more() ) {
384     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
385     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
386     //       if ( aPosition.get() ) {
387     //         if ( int aShapeID = aPosition->GetShapeId() ) {
388     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
389     //             smmap.insert( sm );
390     //         }
391     //       }
392     //     }
393
394     // Do remove
395     if ( isNodes )
396       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
397     else
398       aMesh->RemoveElement( elem );
399     removed++;
400   }
401
402   // Notify sub-meshes about modification
403   if ( !smmap.empty() ) {
404     set< SMESH_subMesh *>::iterator smIt;
405     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
406       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
407   }
408
409   //   // Check if the whole mesh becomes empty
410   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
411   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
412
413   return removed;
414 }
415
416 //================================================================================
417 /*!
418  * \brief Create 0D elements on all nodes of the given object except those
419  *        nodes on which a 0D element already exists.
420  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
421  *                    the all mesh is treated
422  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
423  */
424 //================================================================================
425
426 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
427                                                    TIDSortedElemSet&       all0DElems )
428 {
429   SMDS_ElemIteratorPtr elemIt;
430   vector< const SMDS_MeshElement* > allNodes;
431   if ( elements.empty() )
432   {
433     allNodes.reserve( GetMeshDS()->NbNodes() );
434     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
435     while ( elemIt->more() )
436       allNodes.push_back( elemIt->next() );
437
438     elemIt = elemSetIterator( allNodes );
439   }
440   else
441   {
442     elemIt = elemSetIterator( elements );
443   }
444
445   while ( elemIt->more() )
446   {
447     const SMDS_MeshElement* e = elemIt->next();
448     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
449     while ( nodeIt->more() )
450     {
451       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
452       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
453       if ( it0D->more() )
454         all0DElems.insert( it0D->next() );
455       else {
456         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
457         all0DElems.insert( myLastCreatedElems.Last() );
458       }
459     }
460   }
461 }
462
463 //=======================================================================
464 //function : FindShape
465 //purpose  : Return an index of the shape theElem is on
466 //           or zero if a shape not found
467 //=======================================================================
468
469 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
470 {
471   myLastCreatedElems.Clear();
472   myLastCreatedNodes.Clear();
473
474   SMESHDS_Mesh * aMesh = GetMeshDS();
475   if ( aMesh->ShapeToMesh().IsNull() )
476     return 0;
477
478   int aShapeID = theElem->getshapeId();
479   if ( aShapeID < 1 )
480     return 0;
481
482   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
483     if ( sm->Contains( theElem ))
484       return aShapeID;
485
486   if ( theElem->GetType() == SMDSAbs_Node ) {
487     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
488   }
489   else {
490     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
491   }
492
493   TopoDS_Shape aShape; // the shape a node of theElem is on
494   if ( theElem->GetType() != SMDSAbs_Node )
495   {
496     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
497     while ( nodeIt->more() ) {
498       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
499       if ((aShapeID = node->getshapeId()) > 0) {
500         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
501           if ( sm->Contains( theElem ))
502             return aShapeID;
503           if ( aShape.IsNull() )
504             aShape = aMesh->IndexToShape( aShapeID );
505         }
506       }
507     }
508   }
509
510   // None of nodes is on a proper shape,
511   // find the shape among ancestors of aShape on which a node is
512   if ( !aShape.IsNull() ) {
513     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
514     for ( ; ancIt.More(); ancIt.Next() ) {
515       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
516       if ( sm && sm->Contains( theElem ))
517         return aMesh->ShapeToIndex( ancIt.Value() );
518     }
519   }
520   else
521   {
522     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
523     while ( const SMESHDS_SubMesh* sm = smIt->next() )
524       if ( sm->Contains( theElem ))
525         return sm->GetID();
526   }
527
528   return 0;
529 }
530
531 //=======================================================================
532 //function : IsMedium
533 //purpose  :
534 //=======================================================================
535
536 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
537                                 const SMDSAbs_ElementType typeToCheck)
538 {
539   bool isMedium = false;
540   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
541   while (it->more() && !isMedium ) {
542     const SMDS_MeshElement* elem = it->next();
543     isMedium = elem->IsMediumNode(node);
544   }
545   return isMedium;
546 }
547
548 //=======================================================================
549 //function : shiftNodesQuadTria
550 //purpose  : Shift nodes in the array corresponded to quadratic triangle
551 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
552 //=======================================================================
553
554 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
555 {
556   const SMDS_MeshNode* nd1 = aNodes[0];
557   aNodes[0] = aNodes[1];
558   aNodes[1] = aNodes[2];
559   aNodes[2] = nd1;
560   const SMDS_MeshNode* nd2 = aNodes[3];
561   aNodes[3] = aNodes[4];
562   aNodes[4] = aNodes[5];
563   aNodes[5] = nd2;
564 }
565
566 //=======================================================================
567 //function : nbEdgeConnectivity
568 //purpose  : return number of the edges connected with the theNode.
569 //           if theEdges has connections with the other type of the
570 //           elements, return -1
571 //=======================================================================
572
573 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
574 {
575   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
576   // int nb=0;
577   // while(elemIt->more()) {
578   //   elemIt->next();
579   //   nb++;
580   // }
581   // return nb;
582   return theNode->NbInverseElements();
583 }
584
585 //=======================================================================
586 //function : getNodesFromTwoTria
587 //purpose  : 
588 //=======================================================================
589
590 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
591                                 const SMDS_MeshElement * theTria2,
592                                 vector< const SMDS_MeshNode*>& N1,
593                                 vector< const SMDS_MeshNode*>& N2)
594 {
595   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
596   if ( N1.size() < 6 ) return false;
597   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
598   if ( N2.size() < 6 ) return false;
599
600   int sames[3] = {-1,-1,-1};
601   int nbsames = 0;
602   int i, j;
603   for(i=0; i<3; i++) {
604     for(j=0; j<3; j++) {
605       if(N1[i]==N2[j]) {
606         sames[i] = j;
607         nbsames++;
608         break;
609       }
610     }
611   }
612   if(nbsames!=2) return false;
613   if(sames[0]>-1) {
614     shiftNodesQuadTria(N1);
615     if(sames[1]>-1) {
616       shiftNodesQuadTria(N1);
617     }
618   }
619   i = sames[0] + sames[1] + sames[2];
620   for(; i<2; i++) {
621     shiftNodesQuadTria(N2);
622   }
623   // now we receive following N1 and N2 (using numeration as in the image below)
624   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
625   // i.e. first nodes from both arrays form a new diagonal
626   return true;
627 }
628
629 //=======================================================================
630 //function : InverseDiag
631 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
632 //           but having other common link.
633 //           Return False if args are improper
634 //=======================================================================
635
636 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
637                                     const SMDS_MeshElement * theTria2 )
638 {
639   MESSAGE("InverseDiag");
640   myLastCreatedElems.Clear();
641   myLastCreatedNodes.Clear();
642
643   if (!theTria1 || !theTria2)
644     return false;
645
646   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
647   if (!F1) return false;
648   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
649   if (!F2) return false;
650   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
651       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
652
653     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
654     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
655     //    |/ |                                         | \|
656     //  B +--+ 2                                     B +--+ 2
657
658     // put nodes in array and find out indices of the same ones
659     const SMDS_MeshNode* aNodes [6];
660     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
661     int i = 0;
662     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
663     while ( it->more() ) {
664       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
665
666       if ( i > 2 ) // theTria2
667         // find same node of theTria1
668         for ( int j = 0; j < 3; j++ )
669           if ( aNodes[ i ] == aNodes[ j ]) {
670             sameInd[ j ] = i;
671             sameInd[ i ] = j;
672             break;
673           }
674       // next
675       i++;
676       if ( i == 3 ) {
677         if ( it->more() )
678           return false; // theTria1 is not a triangle
679         it = theTria2->nodesIterator();
680       }
681       if ( i == 6 && it->more() )
682         return false; // theTria2 is not a triangle
683     }
684
685     // find indices of 1,2 and of A,B in theTria1
686     int iA = -1, iB = 0, i1 = 0, i2 = 0;
687     for ( i = 0; i < 6; i++ ) {
688       if ( sameInd [ i ] == -1 ) {
689         if ( i < 3 ) i1 = i;
690         else         i2 = i;
691       }
692       else if (i < 3) {
693         if ( iA >= 0) iB = i;
694         else          iA = i;
695       }
696     }
697     // nodes 1 and 2 should not be the same
698     if ( aNodes[ i1 ] == aNodes[ i2 ] )
699       return false;
700
701     // theTria1: A->2
702     aNodes[ iA ] = aNodes[ i2 ];
703     // theTria2: B->1
704     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
705
706     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
707     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
708
709     return true;
710
711   } // end if(F1 && F2)
712
713   // check case of quadratic faces
714   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
715       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
716     return false;
717   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
718       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
719     return false;
720
721   //       5
722   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
723   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
724   //    |   / |
725   //  7 +  +  + 6
726   //    | /9  |
727   //    |/    |
728   //  4 +--+--+ 3
729   //       8
730
731   vector< const SMDS_MeshNode* > N1;
732   vector< const SMDS_MeshNode* > N2;
733   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
734     return false;
735   // now we receive following N1 and N2 (using numeration as above image)
736   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
737   // i.e. first nodes from both arrays determ new diagonal
738
739   vector< const SMDS_MeshNode*> N1new( N1.size() );
740   vector< const SMDS_MeshNode*> N2new( N2.size() );
741   N1new.back() = N1.back(); // central node of biquadratic
742   N2new.back() = N2.back();
743   N1new[0] = N1[0];  N2new[0] = N1[0];
744   N1new[1] = N2[0];  N2new[1] = N1[1];
745   N1new[2] = N2[1];  N2new[2] = N2[0];
746   N1new[3] = N1[4];  N2new[3] = N1[3];
747   N1new[4] = N2[3];  N2new[4] = N2[5];
748   N1new[5] = N1[5];  N2new[5] = N1[4];
749   // change nodes in faces
750   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
751   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
752
753   // move the central node of biquadratic triangle
754   SMESH_MesherHelper helper( *GetMesh() );
755   for ( int is2nd = 0; is2nd < 2; ++is2nd )
756   {
757     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
758     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
759     if ( nodes.size() < 7 )
760       continue;
761     helper.SetSubShape( tria->getshapeId() );
762     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
763     gp_Pnt xyz;
764     if ( F.IsNull() )
765     {
766       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
767               SMESH_TNodeXYZ( nodes[4] ) +
768               SMESH_TNodeXYZ( nodes[5] )) / 3.;
769     }
770     else
771     {
772       bool checkUV;
773       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
774                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
775                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
776       TopLoc_Location loc;
777       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
778       xyz = S->Value( uv.X(), uv.Y() );
779       xyz.Transform( loc );
780       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
781            nodes[6]->getshapeId() > 0 )
782         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
783     }
784     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
785   }
786   return true;
787 }
788
789 //=======================================================================
790 //function : findTriangles
791 //purpose  : find triangles sharing theNode1-theNode2 link
792 //=======================================================================
793
794 static bool findTriangles(const SMDS_MeshNode *    theNode1,
795                           const SMDS_MeshNode *    theNode2,
796                           const SMDS_MeshElement*& theTria1,
797                           const SMDS_MeshElement*& theTria2)
798 {
799   if ( !theNode1 || !theNode2 ) return false;
800
801   theTria1 = theTria2 = 0;
802
803   set< const SMDS_MeshElement* > emap;
804   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
805   while (it->more()) {
806     const SMDS_MeshElement* elem = it->next();
807     if ( elem->NbCornerNodes() == 3 )
808       emap.insert( elem );
809   }
810   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
811   while (it->more()) {
812     const SMDS_MeshElement* elem = it->next();
813     if ( emap.count( elem )) {
814       if ( !theTria1 )
815       {
816         theTria1 = elem;
817       }
818       else  
819       {
820         theTria2 = elem;
821         // theTria1 must be element with minimum ID
822         if ( theTria2->GetID() < theTria1->GetID() )
823           std::swap( theTria2, theTria1 );
824         return true;
825       }
826     }
827   }
828   return false;
829 }
830
831 //=======================================================================
832 //function : InverseDiag
833 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
834 //           with ones built on the same 4 nodes but having other common link.
835 //           Return false if proper faces not found
836 //=======================================================================
837
838 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
839                                     const SMDS_MeshNode * theNode2)
840 {
841   myLastCreatedElems.Clear();
842   myLastCreatedNodes.Clear();
843
844   MESSAGE( "::InverseDiag()" );
845
846   const SMDS_MeshElement *tr1, *tr2;
847   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
848     return false;
849
850   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
851   if (!F1) return false;
852   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
853   if (!F2) return false;
854   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
855       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
856
857     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
858     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
859     //    |/ |                                    | \|
860     //  B +--+ 2                                B +--+ 2
861
862     // put nodes in array
863     // and find indices of 1,2 and of A in tr1 and of B in tr2
864     int i, iA1 = 0, i1 = 0;
865     const SMDS_MeshNode* aNodes1 [3];
866     SMDS_ElemIteratorPtr it;
867     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
868       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
869       if ( aNodes1[ i ] == theNode1 )
870         iA1 = i; // node A in tr1
871       else if ( aNodes1[ i ] != theNode2 )
872         i1 = i;  // node 1
873     }
874     int iB2 = 0, i2 = 0;
875     const SMDS_MeshNode* aNodes2 [3];
876     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
877       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
878       if ( aNodes2[ i ] == theNode2 )
879         iB2 = i; // node B in tr2
880       else if ( aNodes2[ i ] != theNode1 )
881         i2 = i;  // node 2
882     }
883
884     // nodes 1 and 2 should not be the same
885     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
886       return false;
887
888     // tr1: A->2
889     aNodes1[ iA1 ] = aNodes2[ i2 ];
890     // tr2: B->1
891     aNodes2[ iB2 ] = aNodes1[ i1 ];
892
893     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
894     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
895
896     return true;
897   }
898
899   // check case of quadratic faces
900   return InverseDiag(tr1,tr2);
901 }
902
903 //=======================================================================
904 //function : getQuadrangleNodes
905 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
906 //           fusion of triangles tr1 and tr2 having shared link on
907 //           theNode1 and theNode2
908 //=======================================================================
909
910 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
911                         const SMDS_MeshNode *    theNode1,
912                         const SMDS_MeshNode *    theNode2,
913                         const SMDS_MeshElement * tr1,
914                         const SMDS_MeshElement * tr2 )
915 {
916   if( tr1->NbNodes() != tr2->NbNodes() )
917     return false;
918   // find the 4-th node to insert into tr1
919   const SMDS_MeshNode* n4 = 0;
920   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
921   int i=0;
922   while ( !n4 && i<3 ) {
923     const SMDS_MeshNode * n = cast2Node( it->next() );
924     i++;
925     bool isDiag = ( n == theNode1 || n == theNode2 );
926     if ( !isDiag )
927       n4 = n;
928   }
929   // Make an array of nodes to be in a quadrangle
930   int iNode = 0, iFirstDiag = -1;
931   it = tr1->nodesIterator();
932   i=0;
933   while ( i<3 ) {
934     const SMDS_MeshNode * n = cast2Node( it->next() );
935     i++;
936     bool isDiag = ( n == theNode1 || n == theNode2 );
937     if ( isDiag ) {
938       if ( iFirstDiag < 0 )
939         iFirstDiag = iNode;
940       else if ( iNode - iFirstDiag == 1 )
941         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
942     }
943     else if ( n == n4 ) {
944       return false; // tr1 and tr2 should not have all the same nodes
945     }
946     theQuadNodes[ iNode++ ] = n;
947   }
948   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
949     theQuadNodes[ iNode ] = n4;
950
951   return true;
952 }
953
954 //=======================================================================
955 //function : DeleteDiag
956 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
957 //           with a quadrangle built on the same 4 nodes.
958 //           Return false if proper faces not found
959 //=======================================================================
960
961 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
962                                    const SMDS_MeshNode * theNode2)
963 {
964   myLastCreatedElems.Clear();
965   myLastCreatedNodes.Clear();
966
967   MESSAGE( "::DeleteDiag()" );
968
969   const SMDS_MeshElement *tr1, *tr2;
970   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
971     return false;
972
973   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
974   if (!F1) return false;
975   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
976   if (!F2) return false;
977   SMESHDS_Mesh * aMesh = GetMeshDS();
978
979   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
980       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
981
982     const SMDS_MeshNode* aNodes [ 4 ];
983     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
984       return false;
985
986     const SMDS_MeshElement* newElem = 0;
987     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
988     myLastCreatedElems.Append(newElem);
989     AddToSameGroups( newElem, tr1, aMesh );
990     int aShapeId = tr1->getshapeId();
991     if ( aShapeId )
992       {
993         aMesh->SetMeshElementOnShape( newElem, aShapeId );
994       }
995     aMesh->RemoveElement( tr1 );
996     aMesh->RemoveElement( tr2 );
997
998     return true;
999   }
1000
1001   // check case of quadratic faces
1002   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1003     return false;
1004   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1005     return false;
1006
1007   //       5
1008   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1009   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1010   //    |   / |
1011   //  7 +  +  + 6
1012   //    | /9  |
1013   //    |/    |
1014   //  4 +--+--+ 3
1015   //       8
1016
1017   vector< const SMDS_MeshNode* > N1;
1018   vector< const SMDS_MeshNode* > N2;
1019   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1020     return false;
1021   // now we receive following N1 and N2 (using numeration as above image)
1022   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1023   // i.e. first nodes from both arrays determ new diagonal
1024
1025   const SMDS_MeshNode* aNodes[8];
1026   aNodes[0] = N1[0];
1027   aNodes[1] = N1[1];
1028   aNodes[2] = N2[0];
1029   aNodes[3] = N2[1];
1030   aNodes[4] = N1[3];
1031   aNodes[5] = N2[5];
1032   aNodes[6] = N2[3];
1033   aNodes[7] = N1[5];
1034
1035   const SMDS_MeshElement* newElem = 0;
1036   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1037                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1038   myLastCreatedElems.Append(newElem);
1039   AddToSameGroups( newElem, tr1, aMesh );
1040   int aShapeId = tr1->getshapeId();
1041   if ( aShapeId )
1042     {
1043       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044     }
1045   aMesh->RemoveElement( tr1 );
1046   aMesh->RemoveElement( tr2 );
1047
1048   // remove middle node (9)
1049   GetMeshDS()->RemoveNode( N1[4] );
1050
1051   return true;
1052 }
1053
1054 //=======================================================================
1055 //function : Reorient
1056 //purpose  : Reverse theElement orientation
1057 //=======================================================================
1058
1059 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1060 {
1061   MESSAGE("Reorient");
1062   myLastCreatedElems.Clear();
1063   myLastCreatedNodes.Clear();
1064
1065   if (!theElem)
1066     return false;
1067   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1068   if ( !it || !it->more() )
1069     return false;
1070
1071   const SMDSAbs_ElementType type = theElem->GetType();
1072   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1073     return false;
1074
1075   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1076   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1077   {
1078     const SMDS_VtkVolume* aPolyedre =
1079       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1080     if (!aPolyedre) {
1081       MESSAGE("Warning: bad volumic element");
1082       return false;
1083     }
1084     const int nbFaces = aPolyedre->NbFaces();
1085     vector<const SMDS_MeshNode *> poly_nodes;
1086     vector<int> quantities (nbFaces);
1087
1088     // reverse each face of the polyedre
1089     for (int iface = 1; iface <= nbFaces; iface++) {
1090       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1091       quantities[iface - 1] = nbFaceNodes;
1092
1093       for (inode = nbFaceNodes; inode >= 1; inode--) {
1094         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1095         poly_nodes.push_back(curNode);
1096       }
1097     }
1098     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1099   }
1100   else // other elements
1101   {
1102     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1103     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1104     if ( interlace.empty() )
1105     {
1106       std::reverse( nodes.begin(), nodes.end() ); // polygon
1107     }
1108     else if ( interlace.size() > 1 )
1109     {
1110       SMDS_MeshCell::applyInterlace( interlace, nodes );
1111     }
1112     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1113   }
1114   return false;
1115 }
1116
1117 //================================================================================
1118 /*!
1119  * \brief Reorient faces.
1120  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1121  * \param theDirection - desired direction of normal of \a theFace
1122  * \param theFace - one of \a theFaces that sould be oriented according to
1123  *        \a theDirection and whose orientation defines orientation of other faces
1124  * \return number of reoriented faces.
1125  */
1126 //================================================================================
1127
1128 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1129                                   const gp_Dir&            theDirection,
1130                                   const SMDS_MeshElement * theFace)
1131 {
1132   int nbReori = 0;
1133   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1134
1135   if ( theFaces.empty() )
1136   {
1137     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1138     while ( fIt->more() )
1139       theFaces.insert( theFaces.end(), fIt->next() );
1140   }
1141
1142   // orient theFace according to theDirection
1143   gp_XYZ normal;
1144   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1145   if ( normal * theDirection.XYZ() < 0 )
1146     nbReori += Reorient( theFace );
1147
1148   // Orient other faces
1149
1150   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1151   TIDSortedElemSet avoidSet;
1152   set< SMESH_TLink > checkedLinks;
1153   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1154
1155   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1156     theFaces.erase( theFace );
1157   startFaces.insert( theFace );
1158
1159   int nodeInd1, nodeInd2;
1160   const SMDS_MeshElement*           otherFace;
1161   vector< const SMDS_MeshElement* > facesNearLink;
1162   vector< std::pair< int, int > >   nodeIndsOfFace;
1163
1164   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1165   while ( !startFaces.empty() )
1166   {
1167     startFace = startFaces.begin();
1168     theFace = *startFace;
1169     startFaces.erase( startFace );
1170     if ( !visitedFaces.insert( theFace ).second )
1171       continue;
1172
1173     avoidSet.clear();
1174     avoidSet.insert(theFace);
1175
1176     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1177
1178     const int nbNodes = theFace->NbCornerNodes();
1179     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1180     {
1181       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1182       linkIt_isNew = checkedLinks.insert( link );
1183       if ( !linkIt_isNew.second )
1184       {
1185         // link has already been checked and won't be encountered more
1186         // if the group (theFaces) is manifold
1187         //checkedLinks.erase( linkIt_isNew.first );
1188       }
1189       else
1190       {
1191         facesNearLink.clear();
1192         nodeIndsOfFace.clear();
1193         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1194                                                              theFaces, avoidSet,
1195                                                              &nodeInd1, &nodeInd2 )))
1196           if ( otherFace != theFace)
1197           {
1198             facesNearLink.push_back( otherFace );
1199             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1200             avoidSet.insert( otherFace );
1201           }
1202         if ( facesNearLink.size() > 1 )
1203         {
1204           // NON-MANIFOLD mesh shell !
1205           // select a face most co-directed with theFace,
1206           // other faces won't be visited this time
1207           gp_XYZ NF, NOF;
1208           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1209           double proj, maxProj = -1;
1210           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1211             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1212             if (( proj = Abs( NF * NOF )) > maxProj ) {
1213               maxProj = proj;
1214               otherFace = facesNearLink[i];
1215               nodeInd1  = nodeIndsOfFace[i].first;
1216               nodeInd2  = nodeIndsOfFace[i].second;
1217             }
1218           }
1219           // not to visit rejected faces
1220           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1221             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1222               visitedFaces.insert( facesNearLink[i] );
1223         }
1224         else if ( facesNearLink.size() == 1 )
1225         {
1226           otherFace = facesNearLink[0];
1227           nodeInd1  = nodeIndsOfFace.back().first;
1228           nodeInd2  = nodeIndsOfFace.back().second;
1229         }
1230         if ( otherFace && otherFace != theFace)
1231         {
1232           // link must be reverse in otherFace if orientation ot otherFace
1233           // is same as that of theFace
1234           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1235           {
1236             nbReori += Reorient( otherFace );
1237           }
1238           startFaces.insert( otherFace );
1239         }
1240       }
1241       std::swap( link.first, link.second ); // reverse the link
1242     }
1243   }
1244   return nbReori;
1245 }
1246
1247 //================================================================================
1248 /*!
1249  * \brief Reorient faces basing on orientation of adjacent volumes.
1250  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1251  * \param theVolumes - reference volumes.
1252  * \param theOutsideNormal - to orient faces to have their normal
1253  *        pointing either \a outside or \a inside the adjacent volumes.
1254  * \return number of reoriented faces.
1255  */
1256 //================================================================================
1257
1258 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1259                                       TIDSortedElemSet & theVolumes,
1260                                       const bool         theOutsideNormal)
1261 {
1262   int nbReori = 0;
1263
1264   SMDS_ElemIteratorPtr faceIt;
1265   if ( theFaces.empty() )
1266     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1267   else
1268     faceIt = elemSetIterator( theFaces );
1269
1270   vector< const SMDS_MeshNode* > faceNodes;
1271   TIDSortedElemSet checkedVolumes;
1272   set< const SMDS_MeshNode* > faceNodesSet;
1273   SMDS_VolumeTool volumeTool;
1274
1275   while ( faceIt->more() ) // loop on given faces
1276   {
1277     const SMDS_MeshElement* face = faceIt->next();
1278     if ( face->GetType() != SMDSAbs_Face )
1279       continue;
1280
1281     const int nbCornersNodes = face->NbCornerNodes();
1282     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1283
1284     checkedVolumes.clear();
1285     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1286     while ( vIt->more() )
1287     {
1288       const SMDS_MeshElement* volume = vIt->next();
1289
1290       if ( !checkedVolumes.insert( volume ).second )
1291         continue;
1292       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1293         continue;
1294
1295       // is volume adjacent?
1296       bool allNodesCommon = true;
1297       for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1298         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1299       if ( !allNodesCommon )
1300         continue;
1301
1302       // get nodes of a corresponding volume facet
1303       faceNodesSet.clear();
1304       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1305       volumeTool.Set( volume );
1306       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1307       if ( facetID < 0 ) continue;
1308       volumeTool.SetExternalNormal();
1309       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1310
1311       // compare order of faceNodes and facetNodes
1312       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1313       int iNN[2];
1314       for ( int i = 0; i < 2; ++i )
1315       {
1316         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1317         for ( int iN = 0; iN < nbCornersNodes; ++iN )
1318           if ( faceNodes[ iN ] == n )
1319           {
1320             iNN[ i ] = iN;
1321             break;
1322           }
1323       }
1324       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1325       if ( isOutside != theOutsideNormal )
1326         nbReori += Reorient( face );
1327     }
1328   }  // loop on given faces
1329
1330   return nbReori;
1331 }
1332
1333 //=======================================================================
1334 //function : getBadRate
1335 //purpose  :
1336 //=======================================================================
1337
1338 static double getBadRate (const SMDS_MeshElement*               theElem,
1339                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1340 {
1341   SMESH::Controls::TSequenceOfXYZ P;
1342   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1343     return 1e100;
1344   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1345   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1346 }
1347
1348 //=======================================================================
1349 //function : QuadToTri
1350 //purpose  : Cut quadrangles into triangles.
1351 //           theCrit is used to select a diagonal to cut
1352 //=======================================================================
1353
1354 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1355                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1356 {
1357   myLastCreatedElems.Clear();
1358   myLastCreatedNodes.Clear();
1359
1360   if ( !theCrit.get() )
1361     return false;
1362
1363   SMESHDS_Mesh * aMesh = GetMeshDS();
1364
1365   Handle(Geom_Surface) surface;
1366   SMESH_MesherHelper   helper( *GetMesh() );
1367
1368   TIDSortedElemSet::iterator itElem;
1369   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1370   {
1371     const SMDS_MeshElement* elem = *itElem;
1372     if ( !elem || elem->GetType() != SMDSAbs_Face )
1373       continue;
1374     if ( elem->NbCornerNodes() != 4 )
1375       continue;
1376
1377     // retrieve element nodes
1378     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1379
1380     // compare two sets of possible triangles
1381     double aBadRate1, aBadRate2; // to what extent a set is bad
1382     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1383     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1384     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1385
1386     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1387     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1388     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1389
1390     const int aShapeId = FindShape( elem );
1391     const SMDS_MeshElement* newElem1 = 0;
1392     const SMDS_MeshElement* newElem2 = 0;
1393
1394     if ( !elem->IsQuadratic() ) // split liner quadrangle
1395     {
1396       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1397       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1398       if ( aBadRate1 <= aBadRate2 ) {
1399         // tr1 + tr2 is better
1400         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1401         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1402       }
1403       else {
1404         // tr3 + tr4 is better
1405         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1406         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1407       }
1408     }
1409     else // split quadratic quadrangle
1410     {
1411       helper.SetIsQuadratic( true );
1412       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1413
1414       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1415       if ( aNodes.size() == 9 )
1416       {
1417         helper.SetIsBiQuadratic( true );
1418         if ( aBadRate1 <= aBadRate2 )
1419           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1420         else
1421           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1422       }
1423       // create a new element
1424       if ( aBadRate1 <= aBadRate2 ) {
1425         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1426         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1427       }
1428       else {
1429         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1430         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1431       }
1432     } // quadratic case
1433
1434     // care of a new element
1435
1436     myLastCreatedElems.Append(newElem1);
1437     myLastCreatedElems.Append(newElem2);
1438     AddToSameGroups( newElem1, elem, aMesh );
1439     AddToSameGroups( newElem2, elem, aMesh );
1440
1441     // put a new triangle on the same shape
1442     if ( aShapeId )
1443       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1444     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1445
1446     aMesh->RemoveElement( elem );
1447   }
1448   return true;
1449 }
1450
1451 //=======================================================================
1452 /*!
1453  * \brief Split each of given quadrangles into 4 triangles.
1454  * \param theElems - The faces to be splitted. If empty all faces are split.
1455  */
1456 //=======================================================================
1457
1458 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1459 {
1460   myLastCreatedElems.Clear();
1461   myLastCreatedNodes.Clear();
1462
1463   SMESH_MesherHelper helper( *GetMesh() );
1464   helper.SetElementsOnShape( true );
1465
1466   SMDS_ElemIteratorPtr faceIt;
1467   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1468   else                    faceIt = elemSetIterator( theElems );
1469
1470   bool   checkUV;
1471   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1472   gp_XYZ xyz[9];
1473   vector< const SMDS_MeshNode* > nodes;
1474   SMESHDS_SubMesh*               subMeshDS;
1475   TopoDS_Face                    F;
1476   Handle(Geom_Surface)           surface;
1477   TopLoc_Location                loc;
1478
1479   while ( faceIt->more() )
1480   {
1481     const SMDS_MeshElement* quad = faceIt->next();
1482     if ( !quad || quad->NbCornerNodes() != 4 )
1483       continue;
1484
1485     // get a surface the quad is on
1486
1487     if ( quad->getshapeId() < 1 )
1488     {
1489       F.Nullify();
1490       helper.SetSubShape( 0 );
1491       subMeshDS = 0;
1492     }
1493     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1494     {
1495       helper.SetSubShape( quad->getshapeId() );
1496       if ( !helper.GetSubShape().IsNull() &&
1497            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1498       {
1499         F = TopoDS::Face( helper.GetSubShape() );
1500         surface = BRep_Tool::Surface( F, loc );
1501         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1502       }
1503       else
1504       {
1505         helper.SetSubShape( 0 );
1506         subMeshDS = 0;
1507       }
1508     }
1509
1510     // create a central node
1511
1512     const SMDS_MeshNode* nCentral;
1513     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1514
1515     if ( nodes.size() == 9 )
1516     {
1517       nCentral = nodes.back();
1518     }
1519     else
1520     {
1521       size_t iN = 0;
1522       if ( F.IsNull() )
1523       {
1524         for ( ; iN < nodes.size(); ++iN )
1525           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1526
1527         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1528           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1529
1530         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1531                                    xyz[0], xyz[1], xyz[2], xyz[3],
1532                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1533       }
1534       else
1535       {
1536         for ( ; iN < nodes.size(); ++iN )
1537           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1538
1539         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1540           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1541
1542         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543                                   uv[0], uv[1], uv[2], uv[3],
1544                                   uv[4], uv[5], uv[6], uv[7] );
1545
1546         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1547         xyz[ 8 ] = p.XYZ();
1548       }
1549
1550       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1551                                  uv[8].X(), uv[8].Y() );
1552       myLastCreatedNodes.Append( nCentral );
1553     }
1554
1555     // create 4 triangles
1556
1557     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1558     
1559     helper.SetIsQuadratic  ( nodes.size() > 4 );
1560     helper.SetIsBiQuadratic( nodes.size() == 9 );
1561     if ( helper.GetIsQuadratic() )
1562       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1563
1564     for ( int i = 0; i < 4; ++i )
1565     {
1566       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1567                                                nodes[(i+1)%4],
1568                                                nCentral );
1569       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1570       myLastCreatedElems.Append( tria );
1571     }
1572   }
1573 }
1574
1575 //=======================================================================
1576 //function : BestSplit
1577 //purpose  : Find better diagonal for cutting.
1578 //=======================================================================
1579
1580 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1581                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1582 {
1583   myLastCreatedElems.Clear();
1584   myLastCreatedNodes.Clear();
1585
1586   if (!theCrit.get())
1587     return -1;
1588
1589   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1590     return -1;
1591
1592   if( theQuad->NbNodes()==4 ||
1593       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1594
1595     // retrieve element nodes
1596     const SMDS_MeshNode* aNodes [4];
1597     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1598     int i = 0;
1599     //while (itN->more())
1600     while (i<4) {
1601       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1602     }
1603     // compare two sets of possible triangles
1604     double aBadRate1, aBadRate2; // to what extent a set is bad
1605     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1606     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1607     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1608
1609     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1610     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1611     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1612     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1613     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1614     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1615       return 1; // diagonal 1-3
1616
1617     return 2; // diagonal 2-4
1618   }
1619   return -1;
1620 }
1621
1622 namespace
1623 {
1624   // Methods of splitting volumes into tetra
1625
1626   const int theHexTo5_1[5*4+1] =
1627     {
1628       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1629     };
1630   const int theHexTo5_2[5*4+1] =
1631     {
1632       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1633     };
1634   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1635
1636   const int theHexTo6_1[6*4+1] =
1637     {
1638       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
1639     };
1640   const int theHexTo6_2[6*4+1] =
1641     {
1642       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
1643     };
1644   const int theHexTo6_3[6*4+1] =
1645     {
1646       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
1647     };
1648   const int theHexTo6_4[6*4+1] =
1649     {
1650       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
1651     };
1652   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1653
1654   const int thePyraTo2_1[2*4+1] =
1655     {
1656       0, 1, 2, 4,    0, 2, 3, 4,   -1
1657     };
1658   const int thePyraTo2_2[2*4+1] =
1659     {
1660       1, 2, 3, 4,    1, 3, 0, 4,   -1
1661     };
1662   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1663
1664   const int thePentaTo3_1[3*4+1] =
1665     {
1666       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1667     };
1668   const int thePentaTo3_2[3*4+1] =
1669     {
1670       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1671     };
1672   const int thePentaTo3_3[3*4+1] =
1673     {
1674       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1675     };
1676   const int thePentaTo3_4[3*4+1] =
1677     {
1678       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1679     };
1680   const int thePentaTo3_5[3*4+1] =
1681     {
1682       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1683     };
1684   const int thePentaTo3_6[3*4+1] =
1685     {
1686       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1687     };
1688   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1689                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1690
1691   // Methods of splitting hexahedron into prisms
1692
1693   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1694     {
1695       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
1696     };
1697   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1698     {
1699       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
1700     };
1701   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1702     {
1703       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
1704     };
1705
1706   const int theHexTo2Prisms_BT_1[6*2+1] =
1707     {
1708       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1709     };
1710   const int theHexTo2Prisms_BT_2[6*2+1] =
1711     {
1712       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1713     };
1714   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1715
1716   const int theHexTo2Prisms_LR_1[6*2+1] =
1717     {
1718       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1719     };
1720   const int theHexTo2Prisms_LR_2[6*2+1] =
1721     {
1722       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1723     };
1724   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1725
1726   const int theHexTo2Prisms_FB_1[6*2+1] =
1727     {
1728       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1729     };
1730   const int theHexTo2Prisms_FB_2[6*2+1] =
1731     {
1732       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1733     };
1734   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1735
1736
1737   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1738   {
1739     int _n1, _n2, _n3;
1740     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1741     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1742     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1743                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1744   };
1745   struct TSplitMethod
1746   {
1747     int        _nbSplits;
1748     int        _nbCorners;
1749     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1750     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1751     bool       _ownConn;      //!< to delete _connectivity in destructor
1752     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1753
1754     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1755       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1756     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1757     bool hasFacet( const TTriangleFacet& facet ) const
1758     {
1759       if ( _nbCorners == 4 )
1760       {
1761         const int* tetConn = _connectivity;
1762         for ( ; tetConn[0] >= 0; tetConn += 4 )
1763           if (( facet.contains( tetConn[0] ) +
1764                 facet.contains( tetConn[1] ) +
1765                 facet.contains( tetConn[2] ) +
1766                 facet.contains( tetConn[3] )) == 3 )
1767             return true;
1768       }
1769       else // prism, _nbCorners == 6
1770       {
1771         const int* prismConn = _connectivity;
1772         for ( ; prismConn[0] >= 0; prismConn += 6 )
1773         {
1774           if (( facet.contains( prismConn[0] ) &&
1775                 facet.contains( prismConn[1] ) &&
1776                 facet.contains( prismConn[2] ))
1777               ||
1778               ( facet.contains( prismConn[3] ) &&
1779                 facet.contains( prismConn[4] ) &&
1780                 facet.contains( prismConn[5] )))
1781             return true;
1782         }
1783       }
1784       return false;
1785     }
1786   };
1787
1788   //=======================================================================
1789   /*!
1790    * \brief return TSplitMethod for the given element to split into tetrahedra
1791    */
1792   //=======================================================================
1793
1794   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1795   {
1796     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1797
1798     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1799     // an edge and a face barycenter; tertaherdons are based on triangles and
1800     // a volume barycenter
1801     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1802
1803     // Find out how adjacent volumes are split
1804
1805     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1806     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1807     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1808     {
1809       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1810       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1811       if ( nbNodes < 4 ) continue;
1812
1813       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1814       const int* nInd = vol.GetFaceNodesIndices( iF );
1815       if ( nbNodes == 4 )
1816       {
1817         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1818         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1819         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1820         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1821       }
1822       else
1823       {
1824         int iCom = 0; // common node of triangle faces to split into
1825         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1826         {
1827           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1828                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1829                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1830           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1831                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1832                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1833           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1834           {
1835             triaSplits.push_back( t012 );
1836             triaSplits.push_back( t023 );
1837             break;
1838           }
1839         }
1840       }
1841       if ( !triaSplits.empty() )
1842         hasAdjacentSplits = true;
1843     }
1844
1845     // Among variants of split method select one compliant with adjacent volumes
1846
1847     TSplitMethod method;
1848     if ( !vol.Element()->IsPoly() && !is24TetMode )
1849     {
1850       int nbVariants = 2, nbTet = 0;
1851       const int** connVariants = 0;
1852       switch ( vol.Element()->GetEntityType() )
1853       {
1854       case SMDSEntity_Hexa:
1855       case SMDSEntity_Quad_Hexa:
1856       case SMDSEntity_TriQuad_Hexa:
1857         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1858           connVariants = theHexTo5, nbTet = 5;
1859         else
1860           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1861         break;
1862       case SMDSEntity_Pyramid:
1863       case SMDSEntity_Quad_Pyramid:
1864         connVariants = thePyraTo2;  nbTet = 2;
1865         break;
1866       case SMDSEntity_Penta:
1867       case SMDSEntity_Quad_Penta:
1868         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1869         break;
1870       default:
1871         nbVariants = 0;
1872       }
1873       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1874       {
1875         // check method compliancy with adjacent tetras,
1876         // all found splits must be among facets of tetras described by this method
1877         method = TSplitMethod( nbTet, connVariants[variant] );
1878         if ( hasAdjacentSplits && method._nbSplits > 0 )
1879         {
1880           bool facetCreated = true;
1881           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1882           {
1883             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1884             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1885               facetCreated = method.hasFacet( *facet );
1886           }
1887           if ( !facetCreated )
1888             method = TSplitMethod(0); // incompatible method
1889         }
1890       }
1891     }
1892     if ( method._nbSplits < 1 )
1893     {
1894       // No standard method is applicable, use a generic solution:
1895       // each facet of a volume is split into triangles and
1896       // each of triangles and a volume barycenter form a tetrahedron.
1897
1898       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1899
1900       int* connectivity = new int[ maxTetConnSize + 1 ];
1901       method._connectivity = connectivity;
1902       method._ownConn = true;
1903       method._baryNode = !isHex27; // to create central node or not
1904
1905       int connSize = 0;
1906       int baryCenInd = vol.NbNodes() - int( isHex27 );
1907       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1908       {
1909         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1910         const int*   nInd = vol.GetFaceNodesIndices( iF );
1911         // find common node of triangle facets of tetra to create
1912         int iCommon = 0; // index in linear numeration
1913         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1914         if ( !triaSplits.empty() )
1915         {
1916           // by found facets
1917           const TTriangleFacet* facet = &triaSplits.front();
1918           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1919             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1920                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1921               break;
1922         }
1923         else if ( nbNodes > 3 && !is24TetMode )
1924         {
1925           // find the best method of splitting into triangles by aspect ratio
1926           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1927           map< double, int > badness2iCommon;
1928           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1929           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1930           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1931           {
1932             double badness = 0;
1933             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1934             {
1935               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1936                                       nodes[ iQ*((iLast-1)%nbNodes)],
1937                                       nodes[ iQ*((iLast  )%nbNodes)]);
1938               badness += getBadRate( &tria, aspectRatio );
1939             }
1940             badness2iCommon.insert( make_pair( badness, iCommon ));
1941           }
1942           // use iCommon with lowest badness
1943           iCommon = badness2iCommon.begin()->second;
1944         }
1945         if ( iCommon >= nbNodes )
1946           iCommon = 0; // something wrong
1947
1948         // fill connectivity of tetrahedra based on a current face
1949         int nbTet = nbNodes - 2;
1950         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1951         {
1952           int faceBaryCenInd;
1953           if ( isHex27 )
1954           {
1955             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1956             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1957           }
1958           else
1959           {
1960             method._faceBaryNode[ iF ] = 0;
1961             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1962           }
1963           nbTet = nbNodes;
1964           for ( int i = 0; i < nbTet; ++i )
1965           {
1966             int i1 = i, i2 = (i+1) % nbNodes;
1967             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1968             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1969             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1970             connectivity[ connSize++ ] = faceBaryCenInd;
1971             connectivity[ connSize++ ] = baryCenInd;
1972           }
1973         }
1974         else
1975         {
1976           for ( int i = 0; i < nbTet; ++i )
1977           {
1978             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1979             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1981             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1982             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1983             connectivity[ connSize++ ] = baryCenInd;
1984           }
1985         }
1986         method._nbSplits += nbTet;
1987
1988       } // loop on volume faces
1989
1990       connectivity[ connSize++ ] = -1;
1991
1992     } // end of generic solution
1993
1994     return method;
1995   }
1996   //=======================================================================
1997   /*!
1998    * \brief return TSplitMethod to split haxhedron into prisms
1999    */
2000   //=======================================================================
2001
2002   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2003                                     const int        methodFlags,
2004                                     const int        facetToSplit)
2005   {
2006     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2007     // B, T, L, B, R, F
2008     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2009
2010     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2011     {
2012       static TSplitMethod to4methods[4]; // order BT, LR, FB
2013       if ( to4methods[iF]._nbSplits == 0 )
2014       {
2015         switch ( iF ) {
2016         case 0:
2017           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2018           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2019           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2020           break;
2021         case 1:
2022           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2023           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2024           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2025           break;
2026         case 2:
2027           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2028           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2029           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2030           break;
2031         default: return to4methods[3];
2032         }
2033         to4methods[iF]._nbSplits  = 4;
2034         to4methods[iF]._nbCorners = 6;
2035       }
2036       return to4methods[iF];
2037     }
2038     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2039
2040     TSplitMethod method;
2041
2042     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2043
2044     const int nbVariants = 2, nbSplits = 2;
2045     const int** connVariants = 0;
2046     switch ( iF ) {
2047     case 0: connVariants = theHexTo2Prisms_BT; break;
2048     case 1: connVariants = theHexTo2Prisms_LR; break;
2049     case 2: connVariants = theHexTo2Prisms_FB; break;
2050     default: return method;
2051     }
2052
2053     // look for prisms adjacent via facetToSplit and an opposite one
2054     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2055     {
2056       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2057       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2058       if ( nbNodes != 4 ) return method;
2059
2060       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2061       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2062       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2063       TTriangleFacet* t;
2064       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2065         t = &t012;
2066       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2067         t = &t123;
2068       else
2069         continue;
2070
2071       // there are adjacent prism
2072       for ( int variant = 0; variant < nbVariants; ++variant )
2073       {
2074         // check method compliancy with adjacent prisms,
2075         // the found prism facets must be among facets of prisms described by current method
2076         method._nbSplits     = nbSplits;
2077         method._nbCorners    = 6;
2078         method._connectivity = connVariants[ variant ];
2079         if ( method.hasFacet( *t ))
2080           return method;
2081       }
2082     }
2083
2084     // No adjacent prisms. Select a variant with a best aspect ratio.
2085
2086     double badness[2] = { 0, 0 };
2087     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2088     const SMDS_MeshNode** nodes = vol.GetNodes();
2089     for ( int variant = 0; variant < nbVariants; ++variant )
2090       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2091       {
2092         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2093         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2094
2095         method._connectivity = connVariants[ variant ];
2096         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2097         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2098         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2099
2100         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2101                                 nodes[ t->_n2 ],
2102                                 nodes[ t->_n3 ] );
2103         badness[ variant ] += getBadRate( &tria, aspectRatio );
2104       }
2105     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2106
2107     method._nbSplits     = nbSplits;
2108     method._nbCorners    = 6;
2109     method._connectivity = connVariants[ iBetter ];
2110
2111     return method;
2112   }
2113
2114   //================================================================================
2115   /*!
2116    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2117    */
2118   //================================================================================
2119
2120   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2121                                        const SMDSAbs_GeometryType geom ) const
2122   {
2123     // find the tetrahedron including the three nodes of facet
2124     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2125     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2126     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2127     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2128     while ( volIt1->more() )
2129     {
2130       const SMDS_MeshElement* v = volIt1->next();
2131       if ( v->GetGeomType() != geom )
2132         continue;
2133       const int lastCornerInd = v->NbCornerNodes() - 1;
2134       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2135         continue; // medium node not allowed
2136       const int ind2 = v->GetNodeIndex( n2 );
2137       if ( ind2 < 0 || lastCornerInd < ind2 )
2138         continue;
2139       const int ind3 = v->GetNodeIndex( n3 );
2140       if ( ind3 < 0 || lastCornerInd < ind3 )
2141         continue;
2142       return true;
2143     }
2144     return false;
2145   }
2146
2147   //=======================================================================
2148   /*!
2149    * \brief A key of a face of volume
2150    */
2151   //=======================================================================
2152
2153   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2154   {
2155     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2156     {
2157       TIDSortedNodeSet sortedNodes;
2158       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2159       int nbNodes = vol.NbFaceNodes( iF );
2160       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2161       for ( int i = 0; i < nbNodes; i += iQ )
2162         sortedNodes.insert( fNodes[i] );
2163       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2164       first.first   = (*(n++))->GetID();
2165       first.second  = (*(n++))->GetID();
2166       second.first  = (*(n++))->GetID();
2167       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2168     }
2169   };
2170 } // namespace
2171
2172 //=======================================================================
2173 //function : SplitVolumes
2174 //purpose  : Split volume elements into tetrahedra or prisms.
2175 //           If facet ID < 0, element is split into tetrahedra,
2176 //           else a hexahedron is split into prisms so that the given facet is
2177 //           split into triangles
2178 //=======================================================================
2179
2180 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2181                                      const int            theMethodFlags)
2182 {
2183   SMDS_VolumeTool    volTool;
2184   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2185   fHelper.ToFixNodeParameters( true );
2186
2187   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2188   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2189
2190   SMESH_SequenceOfElemPtr newNodes, newElems;
2191
2192   // map face of volume to it's baricenrtic node
2193   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2194   double bc[3];
2195
2196   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2197   for ( ; elem2facet != theElems.end(); ++elem2facet )
2198   {
2199     const SMDS_MeshElement* elem = elem2facet->first;
2200     const int       facetToSplit = elem2facet->second;
2201     if ( elem->GetType() != SMDSAbs_Volume )
2202       continue;
2203     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2204     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2205       continue;
2206
2207     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2208
2209     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2210                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2211                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2212     if ( splitMethod._nbSplits < 1 ) continue;
2213
2214     // find submesh to add new tetras to
2215     if ( !subMesh || !subMesh->Contains( elem ))
2216     {
2217       int shapeID = FindShape( elem );
2218       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2219       subMesh = GetMeshDS()->MeshElements( shapeID );
2220     }
2221     int iQ;
2222     if ( elem->IsQuadratic() )
2223     {
2224       iQ = 2;
2225       // add quadratic links to the helper
2226       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2227       {
2228         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2229         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2230         for ( int iN = 0; iN < nbN; iN += iQ )
2231           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2232       }
2233       helper.SetIsQuadratic( true );
2234     }
2235     else
2236     {
2237       iQ = 1;
2238       helper.SetIsQuadratic( false );
2239     }
2240     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2241                                         volTool.GetNodes() + elem->NbNodes() );
2242     helper.SetElementsOnShape( true );
2243     if ( splitMethod._baryNode )
2244     {
2245       // make a node at barycenter
2246       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2247       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2248       nodes.push_back( gcNode );
2249       newNodes.Append( gcNode );
2250     }
2251     if ( !splitMethod._faceBaryNode.empty() )
2252     {
2253       // make or find baricentric nodes of faces
2254       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2255       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2256       {
2257         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2258           volFace2BaryNode.insert
2259           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2260         if ( !f_n->second )
2261         {
2262           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2263           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2264         }
2265         nodes.push_back( iF_n->second = f_n->second );
2266       }
2267     }
2268
2269     // make new volumes
2270     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2271     const int* volConn = splitMethod._connectivity;
2272     if ( splitMethod._nbCorners == 4 ) // tetra
2273       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2274         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2275                                                             nodes[ volConn[1] ],
2276                                                             nodes[ volConn[2] ],
2277                                                             nodes[ volConn[3] ]));
2278     else // prisms
2279       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2280         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2281                                                             nodes[ volConn[1] ],
2282                                                             nodes[ volConn[2] ],
2283                                                             nodes[ volConn[3] ],
2284                                                             nodes[ volConn[4] ],
2285                                                             nodes[ volConn[5] ]));
2286
2287     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2288
2289     // Split faces on sides of the split volume
2290
2291     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2292     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2293     {
2294       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2295       if ( nbNodes < 4 ) continue;
2296
2297       // find an existing face
2298       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2299                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2300       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2301                                                                        /*noMedium=*/false))
2302       {
2303         // make triangles
2304         helper.SetElementsOnShape( false );
2305         vector< const SMDS_MeshElement* > triangles;
2306
2307         // find submesh to add new triangles in
2308         if ( !fSubMesh || !fSubMesh->Contains( face ))
2309         {
2310           int shapeID = FindShape( face );
2311           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2312         }
2313         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2314         if ( iF_n != splitMethod._faceBaryNode.end() )
2315         {
2316           const SMDS_MeshNode *baryNode = iF_n->second;
2317           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2318           {
2319             const SMDS_MeshNode* n1 = fNodes[iN];
2320             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2321             const SMDS_MeshNode *n3 = baryNode;
2322             if ( !volTool.IsFaceExternal( iF ))
2323               swap( n2, n3 );
2324             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2325           }
2326           if ( fSubMesh ) // update position of the bary node on geometry
2327           {
2328             if ( subMesh )
2329               subMesh->RemoveNode( baryNode, false );
2330             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2331             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2332             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2333             {
2334               fHelper.SetSubShape( s );
2335               gp_XY uv( 1e100, 1e100 );
2336               double distXYZ[4];
2337               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2338                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2339                    uv.X() < 1e100 )
2340               {
2341                 // node is too far from the surface
2342                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2343                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2344                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2345               }
2346             }
2347           }
2348         }
2349         else
2350         {
2351           // among possible triangles create ones discribed by split method
2352           const int* nInd = volTool.GetFaceNodesIndices( iF );
2353           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2354           int iCom = 0; // common node of triangle faces to split into
2355           list< TTriangleFacet > facets;
2356           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2357           {
2358             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2359                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2360                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2361             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2362                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2363                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2364             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2365             {
2366               facets.push_back( t012 );
2367               facets.push_back( t023 );
2368               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2369                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2370                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2371                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2372               break;
2373             }
2374           }
2375           list< TTriangleFacet >::iterator facet = facets.begin();
2376           if ( facet == facets.end() )
2377             break;
2378           for ( ; facet != facets.end(); ++facet )
2379           {
2380             if ( !volTool.IsFaceExternal( iF ))
2381               swap( facet->_n2, facet->_n3 );
2382             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2383                                                  volNodes[ facet->_n2 ],
2384                                                  volNodes[ facet->_n3 ]));
2385           }
2386         }
2387         for ( int i = 0; i < triangles.size(); ++i )
2388         {
2389           if ( !triangles[i] ) continue;
2390           if ( fSubMesh )
2391             fSubMesh->AddElement( triangles[i]);
2392           newElems.Append( triangles[i] );
2393         }
2394         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2395         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2396
2397       } // while a face based on facet nodes exists
2398     } // loop on volume faces to split them into triangles
2399
2400     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2401
2402     if ( geomType == SMDSEntity_TriQuad_Hexa )
2403     {
2404       // remove medium nodes that could become free
2405       for ( int i = 20; i < volTool.NbNodes(); ++i )
2406         if ( volNodes[i]->NbInverseElements() == 0 )
2407           GetMeshDS()->RemoveNode( volNodes[i] );
2408     }
2409   } // loop on volumes to split
2410   
2411   myLastCreatedNodes = newNodes;
2412   myLastCreatedElems = newElems;
2413 }
2414
2415 //=======================================================================
2416 //function : GetHexaFacetsToSplit
2417 //purpose  : For hexahedra that will be split into prisms, finds facets to
2418 //           split into triangles. Only hexahedra adjacent to the one closest
2419 //           to theFacetNormal.Location() are returned.
2420 //param [in,out] theHexas - the hexahedra
2421 //param [in]     theFacetNormal - facet normal
2422 //param [out]    theFacets - the hexahedra and found facet IDs
2423 //=======================================================================
2424
2425 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2426                                              const gp_Ax1&     theFacetNormal,
2427                                              TFacetOfElem &    theFacets)
2428 {
2429   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2430
2431   // Find a hexa closest to the location of theFacetNormal
2432
2433   const SMDS_MeshElement* startHex;
2434   {
2435     // get SMDS_ElemIteratorPtr on theHexas
2436     typedef const SMDS_MeshElement*                                      TValue;
2437     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2438     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2439     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2440     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2441     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2442       ( new TElemSetIter( theHexas.begin(),
2443                           theHexas.end(),
2444                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2445
2446     SMESH_ElementSearcher* searcher =
2447       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2448
2449     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2450
2451     delete searcher;
2452
2453     if ( !startHex )
2454       throw SALOME_Exception( THIS_METHOD "startHex not found");
2455   }
2456
2457   // Select a facet of startHex by theFacetNormal
2458
2459   SMDS_VolumeTool vTool( startHex );
2460   double norm[3], dot, maxDot = 0;
2461   int facetID = -1;
2462   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2463     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2464     {
2465       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2466       if ( dot > maxDot )
2467       {
2468         facetID = iF;
2469         maxDot = dot;
2470       }
2471     }
2472   if ( facetID < 0 )
2473     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2474
2475   // Fill theFacets starting from facetID of startHex
2476
2477   // facets used for seach of volumes adjacent to already treated ones
2478   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2479   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2480   TFacetMap facetsToCheck;
2481
2482   set<const SMDS_MeshNode*> facetNodes;
2483   const SMDS_MeshElement*   curHex;
2484
2485   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2486
2487   while ( startHex )
2488   {
2489     // move in two directions from startHex via facetID
2490     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2491     {
2492       curHex       = startHex;
2493       int curFacet = facetID;
2494       if ( is2nd ) // do not treat startHex twice
2495       {
2496         vTool.Set( curHex );
2497         if ( vTool.IsFreeFace( curFacet, &curHex ))
2498         {
2499           curHex = 0;
2500         }
2501         else
2502         {
2503           vTool.GetFaceNodes( curFacet, facetNodes );
2504           vTool.Set( curHex );
2505           curFacet = vTool.GetFaceIndex( facetNodes );
2506         }
2507       }
2508       while ( curHex )
2509       {
2510         // store a facet to split
2511         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2512         {
2513           theFacets.insert( make_pair( curHex, -1 ));
2514           break;
2515         }
2516         if ( !allHex && !theHexas.count( curHex ))
2517           break;
2518
2519         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2520           theFacets.insert( make_pair( curHex, curFacet ));
2521         if ( !facetIt2isNew.second )
2522           break;
2523
2524         // remember not-to-split facets in facetsToCheck
2525         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2526         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2527         {
2528           if ( iF == curFacet && iF == oppFacet )
2529             continue;
2530           TVolumeFaceKey facetKey ( vTool, iF );
2531           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2532           pair< TFacetMap::iterator, bool > it2isnew =
2533             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2534           if ( !it2isnew.second )
2535             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2536         }
2537         // pass to a volume adjacent via oppFacet
2538         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2539         {
2540           curHex = 0;
2541         }
2542         else
2543         {
2544           // get a new curFacet
2545           vTool.GetFaceNodes( oppFacet, facetNodes );
2546           vTool.Set( curHex );
2547           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2548         }
2549       }
2550     } // move in two directions from startHex via facetID
2551
2552     // Find a new startHex by facetsToCheck
2553
2554     startHex = 0;
2555     facetID  = -1;
2556     TFacetMap::iterator fIt = facetsToCheck.begin();
2557     while ( !startHex && fIt != facetsToCheck.end() )
2558     {
2559       const TElemFacets&  elemFacets = fIt->second;
2560       const SMDS_MeshElement*    hex = elemFacets.first->first;
2561       int                 splitFacet = elemFacets.first->second;
2562       int               lateralFacet = elemFacets.second;
2563       facetsToCheck.erase( fIt );
2564       fIt = facetsToCheck.begin();
2565
2566       vTool.Set( hex );
2567       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2568            curHex->GetGeomType() != SMDSGeom_HEXA )
2569         continue;
2570       if ( !allHex && !theHexas.count( curHex ))
2571         continue;
2572
2573       startHex = curHex;
2574
2575       // find a facet of startHex to split 
2576
2577       set<const SMDS_MeshNode*> lateralNodes;
2578       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2579       vTool.GetFaceNodes( splitFacet,   facetNodes );
2580       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2581       vTool.Set( startHex );
2582       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2583
2584       // look for a facet of startHex having common nodes with facetNodes
2585       // but not lateralFacet
2586       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2587       {
2588         if ( iF == lateralFacet )
2589           continue;
2590         int nbCommonNodes = 0;
2591         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2592         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2593           nbCommonNodes += facetNodes.count( nn[ iN ]);
2594
2595         if ( nbCommonNodes >= 2 )
2596         {
2597           facetID = iF;
2598           break;
2599         }
2600       }
2601       if ( facetID < 0 )
2602         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2603     }
2604   } //   while ( startHex )
2605 }
2606
2607 //=======================================================================
2608 //function : AddToSameGroups
2609 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2610 //=======================================================================
2611
2612 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2613                                         const SMDS_MeshElement* elemInGroups,
2614                                         SMESHDS_Mesh *          aMesh)
2615 {
2616   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2617   if (!groups.empty()) {
2618     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2619     for ( ; grIt != groups.end(); grIt++ ) {
2620       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2621       if ( group && group->Contains( elemInGroups ))
2622         group->SMDSGroup().Add( elemToAdd );
2623     }
2624   }
2625 }
2626
2627
2628 //=======================================================================
2629 //function : RemoveElemFromGroups
2630 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2631 //=======================================================================
2632 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2633                                              SMESHDS_Mesh *          aMesh)
2634 {
2635   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2636   if (!groups.empty())
2637   {
2638     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2639     for (; GrIt != groups.end(); GrIt++)
2640     {
2641       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2642       if (!grp || grp->IsEmpty()) continue;
2643       grp->SMDSGroup().Remove(removeelem);
2644     }
2645   }
2646 }
2647
2648 //================================================================================
2649 /*!
2650  * \brief Replace elemToRm by elemToAdd in the all groups
2651  */
2652 //================================================================================
2653
2654 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2655                                             const SMDS_MeshElement* elemToAdd,
2656                                             SMESHDS_Mesh *          aMesh)
2657 {
2658   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2659   if (!groups.empty()) {
2660     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2661     for ( ; grIt != groups.end(); grIt++ ) {
2662       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2663       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2664         group->SMDSGroup().Add( elemToAdd );
2665     }
2666   }
2667 }
2668
2669 //================================================================================
2670 /*!
2671  * \brief Replace elemToRm by elemToAdd in the all groups
2672  */
2673 //================================================================================
2674
2675 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2676                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2677                                             SMESHDS_Mesh *                         aMesh)
2678 {
2679   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2680   if (!groups.empty())
2681   {
2682     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2683     for ( ; grIt != groups.end(); grIt++ ) {
2684       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2685       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2686         for ( int i = 0; i < elemToAdd.size(); ++i )
2687           group->SMDSGroup().Add( elemToAdd[ i ] );
2688     }
2689   }
2690 }
2691
2692 //=======================================================================
2693 //function : QuadToTri
2694 //purpose  : Cut quadrangles into triangles.
2695 //           theCrit is used to select a diagonal to cut
2696 //=======================================================================
2697
2698 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2699                                   const bool         the13Diag)
2700 {
2701   myLastCreatedElems.Clear();
2702   myLastCreatedNodes.Clear();
2703
2704   MESSAGE( "::QuadToTri()" );
2705
2706   SMESHDS_Mesh * aMesh = GetMeshDS();
2707
2708   Handle(Geom_Surface) surface;
2709   SMESH_MesherHelper   helper( *GetMesh() );
2710
2711   TIDSortedElemSet::iterator itElem;
2712   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2713     const SMDS_MeshElement* elem = *itElem;
2714     if ( !elem || elem->GetType() != SMDSAbs_Face )
2715       continue;
2716     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2717     if(!isquad) continue;
2718
2719     if(elem->NbNodes()==4) {
2720       // retrieve element nodes
2721       const SMDS_MeshNode* aNodes [4];
2722       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2723       int i = 0;
2724       while ( itN->more() )
2725         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2726
2727       int aShapeId = FindShape( elem );
2728       const SMDS_MeshElement* newElem1 = 0;
2729       const SMDS_MeshElement* newElem2 = 0;
2730       if ( the13Diag ) {
2731         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2732         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2733       }
2734       else {
2735         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2736         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2737       }
2738       myLastCreatedElems.Append(newElem1);
2739       myLastCreatedElems.Append(newElem2);
2740       // put a new triangle on the same shape and add to the same groups
2741       if ( aShapeId )
2742         {
2743           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2744           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2745         }
2746       AddToSameGroups( newElem1, elem, aMesh );
2747       AddToSameGroups( newElem2, elem, aMesh );
2748       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2749       aMesh->RemoveElement( elem );
2750     }
2751
2752     // Quadratic quadrangle
2753
2754     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2755
2756       // get surface elem is on
2757       int aShapeId = FindShape( elem );
2758       if ( aShapeId != helper.GetSubShapeID() ) {
2759         surface.Nullify();
2760         TopoDS_Shape shape;
2761         if ( aShapeId > 0 )
2762           shape = aMesh->IndexToShape( aShapeId );
2763         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2764           TopoDS_Face face = TopoDS::Face( shape );
2765           surface = BRep_Tool::Surface( face );
2766           if ( !surface.IsNull() )
2767             helper.SetSubShape( shape );
2768         }
2769       }
2770
2771       const SMDS_MeshNode* aNodes [8];
2772       const SMDS_MeshNode* inFaceNode = 0;
2773       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2774       int i = 0;
2775       while ( itN->more() ) {
2776         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2777         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2778              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2779         {
2780           inFaceNode = aNodes[ i-1 ];
2781         }
2782       }
2783
2784       // find middle point for (0,1,2,3)
2785       // and create a node in this point;
2786       gp_XYZ p( 0,0,0 );
2787       if ( surface.IsNull() ) {
2788         for(i=0; i<4; i++)
2789           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2790         p /= 4;
2791       }
2792       else {
2793         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2794         gp_XY uv( 0,0 );
2795         for(i=0; i<4; i++)
2796           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2797         uv /= 4.;
2798         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2799       }
2800       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2801       myLastCreatedNodes.Append(newN);
2802
2803       // create a new element
2804       const SMDS_MeshElement* newElem1 = 0;
2805       const SMDS_MeshElement* newElem2 = 0;
2806       if ( the13Diag ) {
2807         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2808                                   aNodes[6], aNodes[7], newN );
2809         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2810                                   newN,      aNodes[4], aNodes[5] );
2811       }
2812       else {
2813         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2814                                   aNodes[7], aNodes[4], newN );
2815         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2816                                   newN,      aNodes[5], aNodes[6] );
2817       }
2818       myLastCreatedElems.Append(newElem1);
2819       myLastCreatedElems.Append(newElem2);
2820       // put a new triangle on the same shape and add to the same groups
2821       if ( aShapeId )
2822         {
2823           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2824           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2825         }
2826       AddToSameGroups( newElem1, elem, aMesh );
2827       AddToSameGroups( newElem2, elem, aMesh );
2828       aMesh->RemoveElement( elem );
2829     }
2830   }
2831
2832   return true;
2833 }
2834
2835 //=======================================================================
2836 //function : getAngle
2837 //purpose  :
2838 //=======================================================================
2839
2840 double getAngle(const SMDS_MeshElement * tr1,
2841                 const SMDS_MeshElement * tr2,
2842                 const SMDS_MeshNode *    n1,
2843                 const SMDS_MeshNode *    n2)
2844 {
2845   double angle = 2. * M_PI; // bad angle
2846
2847   // get normals
2848   SMESH::Controls::TSequenceOfXYZ P1, P2;
2849   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2850        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2851     return angle;
2852   gp_Vec N1,N2;
2853   if(!tr1->IsQuadratic())
2854     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2855   else
2856     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2857   if ( N1.SquareMagnitude() <= gp::Resolution() )
2858     return angle;
2859   if(!tr2->IsQuadratic())
2860     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2861   else
2862     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2863   if ( N2.SquareMagnitude() <= gp::Resolution() )
2864     return angle;
2865
2866   // find the first diagonal node n1 in the triangles:
2867   // take in account a diagonal link orientation
2868   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2869   for ( int t = 0; t < 2; t++ ) {
2870     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2871     int i = 0, iDiag = -1;
2872     while ( it->more()) {
2873       const SMDS_MeshElement *n = it->next();
2874       if ( n == n1 || n == n2 ) {
2875         if ( iDiag < 0)
2876           iDiag = i;
2877         else {
2878           if ( i - iDiag == 1 )
2879             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2880           else
2881             nFirst[ t ] = n;
2882           break;
2883         }
2884       }
2885       i++;
2886     }
2887   }
2888   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2889     N2.Reverse();
2890
2891   angle = N1.Angle( N2 );
2892   //SCRUTE( angle );
2893   return angle;
2894 }
2895
2896 // =================================================
2897 // class generating a unique ID for a pair of nodes
2898 // and able to return nodes by that ID
2899 // =================================================
2900 class LinkID_Gen {
2901 public:
2902
2903   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2904     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2905   {}
2906
2907   long GetLinkID (const SMDS_MeshNode * n1,
2908                   const SMDS_MeshNode * n2) const
2909   {
2910     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2911   }
2912
2913   bool GetNodes (const long             theLinkID,
2914                  const SMDS_MeshNode* & theNode1,
2915                  const SMDS_MeshNode* & theNode2) const
2916   {
2917     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2918     if ( !theNode1 ) return false;
2919     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2920     if ( !theNode2 ) return false;
2921     return true;
2922   }
2923
2924 private:
2925   LinkID_Gen();
2926   const SMESHDS_Mesh* myMesh;
2927   long                myMaxID;
2928 };
2929
2930
2931 //=======================================================================
2932 //function : TriToQuad
2933 //purpose  : Fuse neighbour triangles into quadrangles.
2934 //           theCrit is used to select a neighbour to fuse with.
2935 //           theMaxAngle is a max angle between element normals at which
2936 //           fusion is still performed.
2937 //=======================================================================
2938
2939 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2940                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2941                                   const double                         theMaxAngle)
2942 {
2943   myLastCreatedElems.Clear();
2944   myLastCreatedNodes.Clear();
2945
2946   MESSAGE( "::TriToQuad()" );
2947
2948   if ( !theCrit.get() )
2949     return false;
2950
2951   SMESHDS_Mesh * aMesh = GetMeshDS();
2952
2953   // Prepare data for algo: build
2954   // 1. map of elements with their linkIDs
2955   // 2. map of linkIDs with their elements
2956
2957   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2958   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2959   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2960   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2961
2962   TIDSortedElemSet::iterator itElem;
2963   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2964   {
2965     const SMDS_MeshElement* elem = *itElem;
2966     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2967     bool IsTria = ( elem->NbCornerNodes()==3 );
2968     if (!IsTria) continue;
2969
2970     // retrieve element nodes
2971     const SMDS_MeshNode* aNodes [4];
2972     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2973     int i = 0;
2974     while ( i < 3 )
2975       aNodes[ i++ ] = itN->next();
2976     aNodes[ 3 ] = aNodes[ 0 ];
2977
2978     // fill maps
2979     for ( i = 0; i < 3; i++ ) {
2980       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2981       // check if elements sharing a link can be fused
2982       itLE = mapLi_listEl.find( link );
2983       if ( itLE != mapLi_listEl.end() ) {
2984         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2985           continue;
2986         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2987         //if ( FindShape( elem ) != FindShape( elem2 ))
2988         //  continue; // do not fuse triangles laying on different shapes
2989         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2990           continue; // avoid making badly shaped quads
2991         (*itLE).second.push_back( elem );
2992       }
2993       else {
2994         mapLi_listEl[ link ].push_back( elem );
2995       }
2996       mapEl_setLi [ elem ].insert( link );
2997     }
2998   }
2999   // Clean the maps from the links shared by a sole element, ie
3000   // links to which only one element is bound in mapLi_listEl
3001
3002   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3003     int nbElems = (*itLE).second.size();
3004     if ( nbElems < 2  ) {
3005       const SMDS_MeshElement* elem = (*itLE).second.front();
3006       SMESH_TLink link = (*itLE).first;
3007       mapEl_setLi[ elem ].erase( link );
3008       if ( mapEl_setLi[ elem ].empty() )
3009         mapEl_setLi.erase( elem );
3010     }
3011   }
3012
3013   // Algo: fuse triangles into quadrangles
3014
3015   while ( ! mapEl_setLi.empty() ) {
3016     // Look for the start element:
3017     // the element having the least nb of shared links
3018     const SMDS_MeshElement* startElem = 0;
3019     int minNbLinks = 4;
3020     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3021       int nbLinks = (*itEL).second.size();
3022       if ( nbLinks < minNbLinks ) {
3023         startElem = (*itEL).first;
3024         minNbLinks = nbLinks;
3025         if ( minNbLinks == 1 )
3026           break;
3027       }
3028     }
3029
3030     // search elements to fuse starting from startElem or links of elements
3031     // fused earlyer - startLinks
3032     list< SMESH_TLink > startLinks;
3033     while ( startElem || !startLinks.empty() ) {
3034       while ( !startElem && !startLinks.empty() ) {
3035         // Get an element to start, by a link
3036         SMESH_TLink linkId = startLinks.front();
3037         startLinks.pop_front();
3038         itLE = mapLi_listEl.find( linkId );
3039         if ( itLE != mapLi_listEl.end() ) {
3040           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3041           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3042           for ( ; itE != listElem.end() ; itE++ )
3043             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3044               startElem = (*itE);
3045           mapLi_listEl.erase( itLE );
3046         }
3047       }
3048
3049       if ( startElem ) {
3050         // Get candidates to be fused
3051         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3052         const SMESH_TLink *link12, *link13;
3053         startElem = 0;
3054         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3055         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3056         ASSERT( !setLi.empty() );
3057         set< SMESH_TLink >::iterator itLi;
3058         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3059         {
3060           const SMESH_TLink & link = (*itLi);
3061           itLE = mapLi_listEl.find( link );
3062           if ( itLE == mapLi_listEl.end() )
3063             continue;
3064
3065           const SMDS_MeshElement* elem = (*itLE).second.front();
3066           if ( elem == tr1 )
3067             elem = (*itLE).second.back();
3068           mapLi_listEl.erase( itLE );
3069           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3070             continue;
3071           if ( tr2 ) {
3072             tr3 = elem;
3073             link13 = &link;
3074           }
3075           else {
3076             tr2 = elem;
3077             link12 = &link;
3078           }
3079
3080           // add other links of elem to list of links to re-start from
3081           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3082           set< SMESH_TLink >::iterator it;
3083           for ( it = links.begin(); it != links.end(); it++ ) {
3084             const SMESH_TLink& link2 = (*it);
3085             if ( link2 != link )
3086               startLinks.push_back( link2 );
3087           }
3088         }
3089
3090         // Get nodes of possible quadrangles
3091         const SMDS_MeshNode *n12 [4], *n13 [4];
3092         bool Ok12 = false, Ok13 = false;
3093         const SMDS_MeshNode *linkNode1, *linkNode2;
3094         if(tr2) {
3095           linkNode1 = link12->first;
3096           linkNode2 = link12->second;
3097           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3098             Ok12 = true;
3099         }
3100         if(tr3) {
3101           linkNode1 = link13->first;
3102           linkNode2 = link13->second;
3103           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3104             Ok13 = true;
3105         }
3106
3107         // Choose a pair to fuse
3108         if ( Ok12 && Ok13 ) {
3109           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3110           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3111           double aBadRate12 = getBadRate( &quad12, theCrit );
3112           double aBadRate13 = getBadRate( &quad13, theCrit );
3113           if (  aBadRate13 < aBadRate12 )
3114             Ok12 = false;
3115           else
3116             Ok13 = false;
3117         }
3118
3119         // Make quadrangles
3120         // and remove fused elems and remove links from the maps
3121         mapEl_setLi.erase( tr1 );
3122         if ( Ok12 )
3123         {
3124           mapEl_setLi.erase( tr2 );
3125           mapLi_listEl.erase( *link12 );
3126           if ( tr1->NbNodes() == 3 )
3127           {
3128             const SMDS_MeshElement* newElem = 0;
3129             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3130             myLastCreatedElems.Append(newElem);
3131             AddToSameGroups( newElem, tr1, aMesh );
3132             int aShapeId = tr1->getshapeId();
3133             if ( aShapeId )
3134               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3135             aMesh->RemoveElement( tr1 );
3136             aMesh->RemoveElement( tr2 );
3137           }
3138           else {
3139             vector< const SMDS_MeshNode* > N1;
3140             vector< const SMDS_MeshNode* > N2;
3141             getNodesFromTwoTria(tr1,tr2,N1,N2);
3142             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3143             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3144             // i.e. first nodes from both arrays form a new diagonal
3145             const SMDS_MeshNode* aNodes[8];
3146             aNodes[0] = N1[0];
3147             aNodes[1] = N1[1];
3148             aNodes[2] = N2[0];
3149             aNodes[3] = N2[1];
3150             aNodes[4] = N1[3];
3151             aNodes[5] = N2[5];
3152             aNodes[6] = N2[3];
3153             aNodes[7] = N1[5];
3154             const SMDS_MeshElement* newElem = 0;
3155             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3156               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3157                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3158             else
3159               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3160                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3161             myLastCreatedElems.Append(newElem);
3162             AddToSameGroups( newElem, tr1, aMesh );
3163             int aShapeId = tr1->getshapeId();
3164             if ( aShapeId )
3165               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3166             aMesh->RemoveElement( tr1 );
3167             aMesh->RemoveElement( tr2 );
3168             // remove middle node (9)
3169             if ( N1[4]->NbInverseElements() == 0 )
3170               aMesh->RemoveNode( N1[4] );
3171             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3172               aMesh->RemoveNode( N1[6] );
3173             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3174               aMesh->RemoveNode( N2[6] );
3175           }
3176         }
3177         else if ( Ok13 )
3178         {
3179           mapEl_setLi.erase( tr3 );
3180           mapLi_listEl.erase( *link13 );
3181           if ( tr1->NbNodes() == 3 ) {
3182             const SMDS_MeshElement* newElem = 0;
3183             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3184             myLastCreatedElems.Append(newElem);
3185             AddToSameGroups( newElem, tr1, aMesh );
3186             int aShapeId = tr1->getshapeId();
3187             if ( aShapeId )
3188               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3189             aMesh->RemoveElement( tr1 );
3190             aMesh->RemoveElement( tr3 );
3191           }
3192           else {
3193             vector< const SMDS_MeshNode* > N1;
3194             vector< const SMDS_MeshNode* > N2;
3195             getNodesFromTwoTria(tr1,tr3,N1,N2);
3196             // now we receive following N1 and N2 (using numeration as above image)
3197             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3198             // i.e. first nodes from both arrays form a new diagonal
3199             const SMDS_MeshNode* aNodes[8];
3200             aNodes[0] = N1[0];
3201             aNodes[1] = N1[1];
3202             aNodes[2] = N2[0];
3203             aNodes[3] = N2[1];
3204             aNodes[4] = N1[3];
3205             aNodes[5] = N2[5];
3206             aNodes[6] = N2[3];
3207             aNodes[7] = N1[5];
3208             const SMDS_MeshElement* newElem = 0;
3209             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3210               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3211                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3212             else
3213               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3214                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3215             myLastCreatedElems.Append(newElem);
3216             AddToSameGroups( newElem, tr1, aMesh );
3217             int aShapeId = tr1->getshapeId();
3218             if ( aShapeId )
3219               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3220             aMesh->RemoveElement( tr1 );
3221             aMesh->RemoveElement( tr3 );
3222             // remove middle node (9)
3223             if ( N1[4]->NbInverseElements() == 0 )
3224               aMesh->RemoveNode( N1[4] );
3225             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3226               aMesh->RemoveNode( N1[6] );
3227             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3228               aMesh->RemoveNode( N2[6] );
3229           }
3230         }
3231
3232         // Next element to fuse: the rejected one
3233         if ( tr3 )
3234           startElem = Ok12 ? tr3 : tr2;
3235
3236       } // if ( startElem )
3237     } // while ( startElem || !startLinks.empty() )
3238   } // while ( ! mapEl_setLi.empty() )
3239
3240   return true;
3241 }
3242
3243
3244 /*#define DUMPSO(txt) \
3245 //  cout << txt << endl;
3246 //=============================================================================
3247 //
3248 //
3249 //
3250 //=============================================================================
3251 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3252 {
3253 if ( i1 == i2 )
3254 return;
3255 int tmp = idNodes[ i1 ];
3256 idNodes[ i1 ] = idNodes[ i2 ];
3257 idNodes[ i2 ] = tmp;
3258 gp_Pnt Ptmp = P[ i1 ];
3259 P[ i1 ] = P[ i2 ];
3260 P[ i2 ] = Ptmp;
3261 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3262 }
3263
3264 //=======================================================================
3265 //function : SortQuadNodes
3266 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3267 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3268 //           1 or 2 else 0.
3269 //=======================================================================
3270
3271 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3272 int               idNodes[] )
3273 {
3274   gp_Pnt P[4];
3275   int i;
3276   for ( i = 0; i < 4; i++ ) {
3277     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3278     if ( !n ) return 0;
3279     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3280   }
3281
3282   gp_Vec V1(P[0], P[1]);
3283   gp_Vec V2(P[0], P[2]);
3284   gp_Vec V3(P[0], P[3]);
3285
3286   gp_Vec Cross1 = V1 ^ V2;
3287   gp_Vec Cross2 = V2 ^ V3;
3288
3289   i = 0;
3290   if (Cross1.Dot(Cross2) < 0)
3291   {
3292     Cross1 = V2 ^ V1;
3293     Cross2 = V1 ^ V3;
3294
3295     if (Cross1.Dot(Cross2) < 0)
3296       i = 2;
3297     else
3298       i = 1;
3299     swap ( i, i + 1, idNodes, P );
3300
3301     //     for ( int ii = 0; ii < 4; ii++ ) {
3302     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3303     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3304     //     }
3305   }
3306   return i;
3307 }
3308
3309 //=======================================================================
3310 //function : SortHexaNodes
3311 //purpose  : Set 8 nodes of a hexahedron in a good order.
3312 //           Return success status
3313 //=======================================================================
3314
3315 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3316                                       int               idNodes[] )
3317 {
3318   gp_Pnt P[8];
3319   int i;
3320   DUMPSO( "INPUT: ========================================");
3321   for ( i = 0; i < 8; i++ ) {
3322     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3323     if ( !n ) return false;
3324     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3325     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3326   }
3327   DUMPSO( "========================================");
3328
3329
3330   set<int> faceNodes;  // ids of bottom face nodes, to be found
3331   set<int> checkedId1; // ids of tried 2-nd nodes
3332   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3333   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3334   int iMin, iLoop1 = 0;
3335
3336   // Loop to try the 2-nd nodes
3337
3338   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3339   {
3340     // Find not checked 2-nd node
3341     for ( i = 1; i < 8; i++ )
3342       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3343         int id1 = idNodes[i];
3344         swap ( 1, i, idNodes, P );
3345         checkedId1.insert ( id1 );
3346         break;
3347       }
3348
3349     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3350     // ie that all but meybe one (id3 which is on the same face) nodes
3351     // lay on the same side from the triangle plane.
3352
3353     bool manyInPlane = false; // more than 4 nodes lay in plane
3354     int iLoop2 = 0;
3355     while ( ++iLoop2 < 6 ) {
3356
3357       // get 1-2-3 plane coeffs
3358       Standard_Real A, B, C, D;
3359       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3360       if ( N.SquareMagnitude() > gp::Resolution() )
3361       {
3362         gp_Pln pln ( P[0], N );
3363         pln.Coefficients( A, B, C, D );
3364
3365         // find the node (iMin) closest to pln
3366         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3367         set<int> idInPln;
3368         for ( i = 3; i < 8; i++ ) {
3369           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3370           if ( fabs( dist[i] ) < minDist ) {
3371             minDist = fabs( dist[i] );
3372             iMin = i;
3373           }
3374           if ( fabs( dist[i] ) <= tol )
3375             idInPln.insert( idNodes[i] );
3376         }
3377
3378         // there should not be more than 4 nodes in bottom plane
3379         if ( idInPln.size() > 1 )
3380         {
3381           DUMPSO( "### idInPln.size() = " << idInPln.size());
3382           // idInPlane does not contain the first 3 nodes
3383           if ( manyInPlane || idInPln.size() == 5)
3384             return false; // all nodes in one plane
3385           manyInPlane = true;
3386
3387           // set the 1-st node to be not in plane
3388           for ( i = 3; i < 8; i++ ) {
3389             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3390               DUMPSO( "### Reset 0-th node");
3391               swap( 0, i, idNodes, P );
3392               break;
3393             }
3394           }
3395
3396           // reset to re-check second nodes
3397           leastDist = DBL_MAX;
3398           faceNodes.clear();
3399           checkedId1.clear();
3400           iLoop1 = 0;
3401           break; // from iLoop2;
3402         }
3403
3404         // check that the other 4 nodes are on the same side
3405         bool sameSide = true;
3406         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3407         for ( i = 3; sameSide && i < 8; i++ ) {
3408           if ( i != iMin )
3409             sameSide = ( isNeg == dist[i] <= 0.);
3410         }
3411
3412         // keep best solution
3413         if ( sameSide && minDist < leastDist ) {
3414           leastDist = minDist;
3415           faceNodes.clear();
3416           faceNodes.insert( idNodes[ 1 ] );
3417           faceNodes.insert( idNodes[ 2 ] );
3418           faceNodes.insert( idNodes[ iMin ] );
3419           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3420                   << " leastDist = " << leastDist);
3421           if ( leastDist <= DBL_MIN )
3422             break;
3423         }
3424       }
3425
3426       // set next 3-d node to check
3427       int iNext = 2 + iLoop2;
3428       if ( iNext < 8 ) {
3429         DUMPSO( "Try 2-nd");
3430         swap ( 2, iNext, idNodes, P );
3431       }
3432     } // while ( iLoop2 < 6 )
3433   } // iLoop1
3434
3435   if ( faceNodes.empty() ) return false;
3436
3437   // Put the faceNodes in proper places
3438   for ( i = 4; i < 8; i++ ) {
3439     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3440       // find a place to put
3441       int iTo = 1;
3442       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3443         iTo++;
3444       DUMPSO( "Set faceNodes");
3445       swap ( iTo, i, idNodes, P );
3446     }
3447   }
3448
3449
3450   // Set nodes of the found bottom face in good order
3451   DUMPSO( " Found bottom face: ");
3452   i = SortQuadNodes( theMesh, idNodes );
3453   if ( i ) {
3454     gp_Pnt Ptmp = P[ i ];
3455     P[ i ] = P[ i+1 ];
3456     P[ i+1 ] = Ptmp;
3457   }
3458   //   else
3459   //     for ( int ii = 0; ii < 4; ii++ ) {
3460   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3461   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3462   //    }
3463
3464   // Gravity center of the top and bottom faces
3465   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3466   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3467
3468   // Get direction from the bottom to the top face
3469   gp_Vec upDir ( aGCb, aGCt );
3470   Standard_Real upDirSize = upDir.Magnitude();
3471   if ( upDirSize <= gp::Resolution() ) return false;
3472   upDir / upDirSize;
3473
3474   // Assure that the bottom face normal points up
3475   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3476   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3477   if ( Nb.Dot( upDir ) < 0 ) {
3478     DUMPSO( "Reverse bottom face");
3479     swap( 1, 3, idNodes, P );
3480   }
3481
3482   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3483   Standard_Real minDist = DBL_MAX;
3484   for ( i = 4; i < 8; i++ ) {
3485     // projection of P[i] to the plane defined by P[0] and upDir
3486     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3487     Standard_Real sqDist = P[0].SquareDistance( Pp );
3488     if ( sqDist < minDist ) {
3489       minDist = sqDist;
3490       iMin = i;
3491     }
3492   }
3493   DUMPSO( "Set 4-th");
3494   swap ( 4, iMin, idNodes, P );
3495
3496   // Set nodes of the top face in good order
3497   DUMPSO( "Sort top face");
3498   i = SortQuadNodes( theMesh, &idNodes[4] );
3499   if ( i ) {
3500     i += 4;
3501     gp_Pnt Ptmp = P[ i ];
3502     P[ i ] = P[ i+1 ];
3503     P[ i+1 ] = Ptmp;
3504   }
3505
3506   // Assure that direction of the top face normal is from the bottom face
3507   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3508   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3509   if ( Nt.Dot( upDir ) < 0 ) {
3510     DUMPSO( "Reverse top face");
3511     swap( 5, 7, idNodes, P );
3512   }
3513
3514   //   DUMPSO( "OUTPUT: ========================================");
3515   //   for ( i = 0; i < 8; i++ ) {
3516   //     float *p = ugrid->GetPoint(idNodes[i]);
3517   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3518   //   }
3519
3520   return true;
3521 }*/
3522
3523 //================================================================================
3524 /*!
3525  * \brief Return nodes linked to the given one
3526  * \param theNode - the node
3527  * \param linkedNodes - the found nodes
3528  * \param type - the type of elements to check
3529  *
3530  * Medium nodes are ignored
3531  */
3532 //================================================================================
3533
3534 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3535                                        TIDSortedElemSet &   linkedNodes,
3536                                        SMDSAbs_ElementType  type )
3537 {
3538   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3539   while ( elemIt->more() )
3540   {
3541     const SMDS_MeshElement* elem = elemIt->next();
3542     if(elem->GetType() == SMDSAbs_0DElement)
3543       continue;
3544
3545     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3546     if ( elem->GetType() == SMDSAbs_Volume )
3547     {
3548       SMDS_VolumeTool vol( elem );
3549       while ( nodeIt->more() ) {
3550         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3551         if ( theNode != n && vol.IsLinked( theNode, n ))
3552           linkedNodes.insert( n );
3553       }
3554     }
3555     else
3556     {
3557       for ( int i = 0; nodeIt->more(); ++i ) {
3558         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3559         if ( n == theNode ) {
3560           int iBefore = i - 1;
3561           int iAfter  = i + 1;
3562           if ( elem->IsQuadratic() ) {
3563             int nb = elem->NbNodes() / 2;
3564             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3565             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3566           }
3567           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3568           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3569         }
3570       }
3571     }
3572   }
3573 }
3574
3575 //=======================================================================
3576 //function : laplacianSmooth
3577 //purpose  : pulls theNode toward the center of surrounding nodes directly
3578 //           connected to that node along an element edge
3579 //=======================================================================
3580
3581 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3582                      const Handle(Geom_Surface)&          theSurface,
3583                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3584 {
3585   // find surrounding nodes
3586
3587   TIDSortedElemSet nodeSet;
3588   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3589
3590   // compute new coodrs
3591
3592   double coord[] = { 0., 0., 0. };
3593   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3594   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3595     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3596     if ( theSurface.IsNull() ) { // smooth in 3D
3597       coord[0] += node->X();
3598       coord[1] += node->Y();
3599       coord[2] += node->Z();
3600     }
3601     else { // smooth in 2D
3602       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3603       gp_XY* uv = theUVMap[ node ];
3604       coord[0] += uv->X();
3605       coord[1] += uv->Y();
3606     }
3607   }
3608   int nbNodes = nodeSet.size();
3609   if ( !nbNodes )
3610     return;
3611   coord[0] /= nbNodes;
3612   coord[1] /= nbNodes;
3613
3614   if ( !theSurface.IsNull() ) {
3615     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3616     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3617     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3618     coord[0] = p3d.X();
3619     coord[1] = p3d.Y();
3620     coord[2] = p3d.Z();
3621   }
3622   else
3623     coord[2] /= nbNodes;
3624
3625   // move node
3626
3627   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3628 }
3629
3630 //=======================================================================
3631 //function : centroidalSmooth
3632 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3633 //           surrounding elements
3634 //=======================================================================
3635
3636 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3637                       const Handle(Geom_Surface)&          theSurface,
3638                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3639 {
3640   gp_XYZ aNewXYZ(0.,0.,0.);
3641   SMESH::Controls::Area anAreaFunc;
3642   double totalArea = 0.;
3643   int nbElems = 0;
3644
3645   // compute new XYZ
3646
3647   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3648   while ( elemIt->more() )
3649   {
3650     const SMDS_MeshElement* elem = elemIt->next();
3651     nbElems++;
3652
3653     gp_XYZ elemCenter(0.,0.,0.);
3654     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3655     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3656     int nn = elem->NbNodes();
3657     if(elem->IsQuadratic()) nn = nn/2;
3658     int i=0;
3659     //while ( itN->more() ) {
3660     while ( i<nn ) {
3661       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3662       i++;
3663       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3664       aNodePoints.push_back( aP );
3665       if ( !theSurface.IsNull() ) { // smooth in 2D
3666         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3667         gp_XY* uv = theUVMap[ aNode ];
3668         aP.SetCoord( uv->X(), uv->Y(), 0. );
3669       }
3670       elemCenter += aP;
3671     }
3672     double elemArea = anAreaFunc.GetValue( aNodePoints );
3673     totalArea += elemArea;
3674     elemCenter /= nn;
3675     aNewXYZ += elemCenter * elemArea;
3676   }
3677   aNewXYZ /= totalArea;
3678   if ( !theSurface.IsNull() ) {
3679     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3680     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3681   }
3682
3683   // move node
3684
3685   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3686 }
3687
3688 //=======================================================================
3689 //function : getClosestUV
3690 //purpose  : return UV of closest projection
3691 //=======================================================================
3692
3693 static bool getClosestUV (Extrema_GenExtPS& projector,
3694                           const gp_Pnt&     point,
3695                           gp_XY &           result)
3696 {
3697   projector.Perform( point );
3698   if ( projector.IsDone() ) {
3699     double u, v, minVal = DBL_MAX;
3700     for ( int i = projector.NbExt(); i > 0; i-- )
3701       if ( projector.SquareDistance( i ) < minVal ) {
3702         minVal = projector.SquareDistance( i );
3703         projector.Point( i ).Parameter( u, v );
3704       }
3705     result.SetCoord( u, v );
3706     return true;
3707   }
3708   return false;
3709 }
3710
3711 //=======================================================================
3712 //function : Smooth
3713 //purpose  : Smooth theElements during theNbIterations or until a worst
3714 //           element has aspect ratio <= theTgtAspectRatio.
3715 //           Aspect Ratio varies in range [1.0, inf].
3716 //           If theElements is empty, the whole mesh is smoothed.
3717 //           theFixedNodes contains additionally fixed nodes. Nodes built
3718 //           on edges and boundary nodes are always fixed.
3719 //=======================================================================
3720
3721 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3722                                set<const SMDS_MeshNode*> & theFixedNodes,
3723                                const SmoothMethod          theSmoothMethod,
3724                                const int                   theNbIterations,
3725                                double                      theTgtAspectRatio,
3726                                const bool                  the2D)
3727 {
3728   myLastCreatedElems.Clear();
3729   myLastCreatedNodes.Clear();
3730
3731   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3732
3733   if ( theTgtAspectRatio < 1.0 )
3734     theTgtAspectRatio = 1.0;
3735
3736   const double disttol = 1.e-16;
3737
3738   SMESH::Controls::AspectRatio aQualityFunc;
3739
3740   SMESHDS_Mesh* aMesh = GetMeshDS();
3741
3742   if ( theElems.empty() ) {
3743     // add all faces to theElems
3744     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3745     while ( fIt->more() ) {
3746       const SMDS_MeshElement* face = fIt->next();
3747       theElems.insert( theElems.end(), face );
3748     }
3749   }
3750   // get all face ids theElems are on
3751   set< int > faceIdSet;
3752   TIDSortedElemSet::iterator itElem;
3753   if ( the2D )
3754     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3755       int fId = FindShape( *itElem );
3756       // check that corresponding submesh exists and a shape is face
3757       if (fId &&
3758           faceIdSet.find( fId ) == faceIdSet.end() &&
3759           aMesh->MeshElements( fId )) {
3760         TopoDS_Shape F = aMesh->IndexToShape( fId );
3761         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3762           faceIdSet.insert( fId );
3763       }
3764     }
3765   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3766
3767   // ===============================================
3768   // smooth elements on each TopoDS_Face separately
3769   // ===============================================
3770
3771   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3772   for ( ; fId != faceIdSet.rend(); ++fId ) {
3773     // get face surface and submesh
3774     Handle(Geom_Surface) surface;
3775     SMESHDS_SubMesh* faceSubMesh = 0;
3776     TopoDS_Face face;
3777     double fToler2 = 0, f,l;
3778     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3779     bool isUPeriodic = false, isVPeriodic = false;
3780     if ( *fId ) {
3781       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3782       surface = BRep_Tool::Surface( face );
3783       faceSubMesh = aMesh->MeshElements( *fId );
3784       fToler2 = BRep_Tool::Tolerance( face );
3785       fToler2 *= fToler2 * 10.;
3786       isUPeriodic = surface->IsUPeriodic();
3787       if ( isUPeriodic )
3788         surface->UPeriod();
3789       isVPeriodic = surface->IsVPeriodic();
3790       if ( isVPeriodic )
3791         surface->VPeriod();
3792       surface->Bounds( u1, u2, v1, v2 );
3793     }
3794     // ---------------------------------------------------------
3795     // for elements on a face, find movable and fixed nodes and
3796     // compute UV for them
3797     // ---------------------------------------------------------
3798     bool checkBoundaryNodes = false;
3799     bool isQuadratic = false;
3800     set<const SMDS_MeshNode*> setMovableNodes;
3801     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3802     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3803     list< const SMDS_MeshElement* > elemsOnFace;
3804
3805     Extrema_GenExtPS projector;
3806     GeomAdaptor_Surface surfAdaptor;
3807     if ( !surface.IsNull() ) {
3808       surfAdaptor.Load( surface );
3809       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3810     }
3811     int nbElemOnFace = 0;
3812     itElem = theElems.begin();
3813     // loop on not yet smoothed elements: look for elems on a face
3814     while ( itElem != theElems.end() ) {
3815       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3816         break; // all elements found
3817
3818       const SMDS_MeshElement* elem = *itElem;
3819       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3820            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3821         ++itElem;
3822         continue;
3823       }
3824       elemsOnFace.push_back( elem );
3825       theElems.erase( itElem++ );
3826       nbElemOnFace++;
3827
3828       if ( !isQuadratic )
3829         isQuadratic = elem->IsQuadratic();
3830
3831       // get movable nodes of elem
3832       const SMDS_MeshNode* node;
3833       SMDS_TypeOfPosition posType;
3834       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3835       int nn = 0, nbn =  elem->NbNodes();
3836       if(elem->IsQuadratic())
3837         nbn = nbn/2;
3838       while ( nn++ < nbn ) {
3839         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3840         const SMDS_PositionPtr& pos = node->GetPosition();
3841         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3842         if (posType != SMDS_TOP_EDGE &&
3843             posType != SMDS_TOP_VERTEX &&
3844             theFixedNodes.find( node ) == theFixedNodes.end())
3845         {
3846           // check if all faces around the node are on faceSubMesh
3847           // because a node on edge may be bound to face
3848           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3849           bool all = true;
3850           if ( faceSubMesh ) {
3851             while ( eIt->more() && all ) {
3852               const SMDS_MeshElement* e = eIt->next();
3853               all = faceSubMesh->Contains( e );
3854             }
3855           }
3856           if ( all )
3857             setMovableNodes.insert( node );
3858           else
3859             checkBoundaryNodes = true;
3860         }
3861         if ( posType == SMDS_TOP_3DSPACE )
3862           checkBoundaryNodes = true;
3863       }
3864
3865       if ( surface.IsNull() )
3866         continue;
3867
3868       // get nodes to check UV
3869       list< const SMDS_MeshNode* > uvCheckNodes;
3870       itN = elem->nodesIterator();
3871       nn = 0; nbn =  elem->NbNodes();
3872       if(elem->IsQuadratic())
3873         nbn = nbn/2;
3874       while ( nn++ < nbn ) {
3875         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3876         if ( uvMap.find( node ) == uvMap.end() )
3877           uvCheckNodes.push_back( node );
3878         // add nodes of elems sharing node
3879         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3880         //         while ( eIt->more() ) {
3881         //           const SMDS_MeshElement* e = eIt->next();
3882         //           if ( e != elem ) {
3883         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3884         //             while ( nIt->more() ) {
3885         //               const SMDS_MeshNode* n =
3886         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3887         //               if ( uvMap.find( n ) == uvMap.end() )
3888         //                 uvCheckNodes.push_back( n );
3889         //             }
3890         //           }
3891         //         }
3892       }
3893       // check UV on face
3894       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3895       for ( ; n != uvCheckNodes.end(); ++n ) {
3896         node = *n;
3897         gp_XY uv( 0, 0 );
3898         const SMDS_PositionPtr& pos = node->GetPosition();
3899         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3900         // get existing UV
3901         switch ( posType ) {
3902         case SMDS_TOP_FACE: {
3903           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3904           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3905           break;
3906         }
3907         case SMDS_TOP_EDGE: {
3908           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3909           Handle(Geom2d_Curve) pcurve;
3910           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3911             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3912           if ( !pcurve.IsNull() ) {
3913             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3914             uv = pcurve->Value( u ).XY();
3915           }
3916           break;
3917         }
3918         case SMDS_TOP_VERTEX: {
3919           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3920           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3921             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3922           break;
3923         }
3924         default:;
3925         }
3926         // check existing UV
3927         bool project = true;
3928         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3929         double dist1 = DBL_MAX, dist2 = 0;
3930         if ( posType != SMDS_TOP_3DSPACE ) {
3931           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3932           project = dist1 > fToler2;
3933         }
3934         if ( project ) { // compute new UV
3935           gp_XY newUV;
3936           if ( !getClosestUV( projector, pNode, newUV )) {
3937             MESSAGE("Node Projection Failed " << node);
3938           }
3939           else {
3940             if ( isUPeriodic )
3941               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3942             if ( isVPeriodic )
3943               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3944             // check new UV
3945             if ( posType != SMDS_TOP_3DSPACE )
3946               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3947             if ( dist2 < dist1 )
3948               uv = newUV;
3949           }
3950         }
3951         // store UV in the map
3952         listUV.push_back( uv );
3953         uvMap.insert( make_pair( node, &listUV.back() ));
3954       }
3955     } // loop on not yet smoothed elements
3956
3957     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3958       checkBoundaryNodes = true;
3959
3960     // fix nodes on mesh boundary
3961
3962     if ( checkBoundaryNodes ) {
3963       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3964       map< SMESH_TLink, int >::iterator link_nb;
3965       // put all elements links to linkNbMap
3966       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3967       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3968         const SMDS_MeshElement* elem = (*elemIt);
3969         int nbn =  elem->NbCornerNodes();
3970         // loop on elem links: insert them in linkNbMap
3971         for ( int iN = 0; iN < nbn; ++iN ) {
3972           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3973           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3974           SMESH_TLink link( n1, n2 );
3975           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3976           link_nb->second++;
3977         }
3978       }
3979       // remove nodes that are in links encountered only once from setMovableNodes
3980       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3981         if ( link_nb->second == 1 ) {
3982           setMovableNodes.erase( link_nb->first.node1() );
3983           setMovableNodes.erase( link_nb->first.node2() );
3984         }
3985       }
3986     }
3987
3988     // -----------------------------------------------------
3989     // for nodes on seam edge, compute one more UV ( uvMap2 );
3990     // find movable nodes linked to nodes on seam and which
3991     // are to be smoothed using the second UV ( uvMap2 )
3992     // -----------------------------------------------------
3993
3994     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3995     if ( !surface.IsNull() ) {
3996       TopExp_Explorer eExp( face, TopAbs_EDGE );
3997       for ( ; eExp.More(); eExp.Next() ) {
3998         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3999         if ( !BRep_Tool::IsClosed( edge, face ))
4000           continue;
4001         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4002         if ( !sm ) continue;
4003         // find out which parameter varies for a node on seam
4004         double f,l;
4005         gp_Pnt2d uv1, uv2;
4006         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4007         if ( pcurve.IsNull() ) continue;
4008         uv1 = pcurve->Value( f );
4009         edge.Reverse();
4010         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4011         if ( pcurve.IsNull() ) continue;
4012         uv2 = pcurve->Value( f );
4013         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4014         // assure uv1 < uv2
4015         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
4016           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
4017         }
4018         // get nodes on seam and its vertices
4019         list< const SMDS_MeshNode* > seamNodes;
4020         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4021         while ( nSeamIt->more() ) {
4022           const SMDS_MeshNode* node = nSeamIt->next();
4023           if ( !isQuadratic || !IsMedium( node ))
4024             seamNodes.push_back( node );
4025         }
4026         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4027         for ( ; vExp.More(); vExp.Next() ) {
4028           sm = aMesh->MeshElements( vExp.Current() );
4029           if ( sm ) {
4030             nSeamIt = sm->GetNodes();
4031             while ( nSeamIt->more() )
4032               seamNodes.push_back( nSeamIt->next() );
4033           }
4034         }
4035         // loop on nodes on seam
4036         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4037         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4038           const SMDS_MeshNode* nSeam = *noSeIt;
4039           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4040           if ( n_uv == uvMap.end() )
4041             continue;
4042           // set the first UV
4043           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4044           // set the second UV
4045           listUV.push_back( *n_uv->second );
4046           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4047           if ( uvMap2.empty() )
4048             uvMap2 = uvMap; // copy the uvMap contents
4049           uvMap2[ nSeam ] = &listUV.back();
4050
4051           // collect movable nodes linked to ones on seam in nodesNearSeam
4052           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4053           while ( eIt->more() ) {
4054             const SMDS_MeshElement* e = eIt->next();
4055             int nbUseMap1 = 0, nbUseMap2 = 0;
4056             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4057             int nn = 0, nbn =  e->NbNodes();
4058             if(e->IsQuadratic()) nbn = nbn/2;
4059             while ( nn++ < nbn )
4060             {
4061               const SMDS_MeshNode* n =
4062                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4063               if (n == nSeam ||
4064                   setMovableNodes.find( n ) == setMovableNodes.end() )
4065                 continue;
4066               // add only nodes being closer to uv2 than to uv1
4067               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4068                            0.5 * ( n->Y() + nSeam->Y() ),
4069                            0.5 * ( n->Z() + nSeam->Z() ));
4070               gp_XY uv;
4071               getClosestUV( projector, pMid, uv );
4072               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
4073                 nodesNearSeam.insert( n );
4074                 nbUseMap2++;
4075               }
4076               else
4077                 nbUseMap1++;
4078             }
4079             // for centroidalSmooth all element nodes must
4080             // be on one side of a seam
4081             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4082               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4083               nn = 0;
4084               while ( nn++ < nbn ) {
4085                 const SMDS_MeshNode* n =
4086                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4087                 setMovableNodes.erase( n );
4088               }
4089             }
4090           }
4091         } // loop on nodes on seam
4092       } // loop on edge of a face
4093     } // if ( !face.IsNull() )
4094
4095     if ( setMovableNodes.empty() ) {
4096       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4097       continue; // goto next face
4098     }
4099
4100     // -------------
4101     // SMOOTHING //
4102     // -------------
4103
4104     int it = -1;
4105     double maxRatio = -1., maxDisplacement = -1.;
4106     set<const SMDS_MeshNode*>::iterator nodeToMove;
4107     for ( it = 0; it < theNbIterations; it++ ) {
4108       maxDisplacement = 0.;
4109       nodeToMove = setMovableNodes.begin();
4110       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4111         const SMDS_MeshNode* node = (*nodeToMove);
4112         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4113
4114         // smooth
4115         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4116         if ( theSmoothMethod == LAPLACIAN )
4117           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4118         else
4119           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4120
4121         // node displacement
4122         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4123         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4124         if ( aDispl > maxDisplacement )
4125           maxDisplacement = aDispl;
4126       }
4127       // no node movement => exit
4128       //if ( maxDisplacement < 1.e-16 ) {
4129       if ( maxDisplacement < disttol ) {
4130         MESSAGE("-- no node movement --");
4131         break;
4132       }
4133
4134       // check elements quality
4135       maxRatio  = 0;
4136       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4137       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4138         const SMDS_MeshElement* elem = (*elemIt);
4139         if ( !elem || elem->GetType() != SMDSAbs_Face )
4140           continue;
4141         SMESH::Controls::TSequenceOfXYZ aPoints;
4142         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4143           double aValue = aQualityFunc.GetValue( aPoints );
4144           if ( aValue > maxRatio )
4145             maxRatio = aValue;
4146         }
4147       }
4148       if ( maxRatio <= theTgtAspectRatio ) {
4149         MESSAGE("-- quality achived --");
4150         break;
4151       }
4152       if (it+1 == theNbIterations) {
4153         MESSAGE("-- Iteration limit exceeded --");
4154       }
4155     } // smoothing iterations
4156
4157     MESSAGE(" Face id: " << *fId <<
4158             " Nb iterstions: " << it <<
4159             " Displacement: " << maxDisplacement <<
4160             " Aspect Ratio " << maxRatio);
4161
4162     // ---------------------------------------
4163     // new nodes positions are computed,
4164     // record movement in DS and set new UV
4165     // ---------------------------------------
4166     nodeToMove = setMovableNodes.begin();
4167     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4168       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4169       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4170       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4171       if ( node_uv != uvMap.end() ) {
4172         gp_XY* uv = node_uv->second;
4173         node->SetPosition
4174           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4175       }
4176     }
4177
4178     // move medium nodes of quadratic elements
4179     if ( isQuadratic )
4180     {
4181       SMESH_MesherHelper helper( *GetMesh() );
4182       helper.SetSubShape( face );
4183       vector<const SMDS_MeshNode*> nodes;
4184       bool checkUV;
4185       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4186       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4187       {
4188         const SMDS_MeshElement* QF = *elemIt;
4189         if ( QF->IsQuadratic() )
4190         {
4191           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4192                         SMDS_MeshElement::iterator() );
4193           nodes.push_back( nodes[0] );
4194           gp_Pnt xyz;
4195           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4196           {
4197             if ( !surface.IsNull() )
4198             {
4199               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4200               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4201               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4202               xyz = surface->Value( uv.X(), uv.Y() );
4203             }
4204             else {
4205               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4206             }
4207             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4208               // we have to move a medium node
4209               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4210           }
4211         }
4212       }
4213     }
4214
4215   } // loop on face ids
4216
4217 }
4218
4219 //=======================================================================
4220 //function : isReverse
4221 //purpose  : Return true if normal of prevNodes is not co-directied with
4222 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4223 //           iNotSame is where prevNodes and nextNodes are different.
4224 //           If result is true then future volume orientation is OK
4225 //=======================================================================
4226
4227 static bool isReverse(const SMDS_MeshElement*             face,
4228                       const vector<const SMDS_MeshNode*>& prevNodes,
4229                       const vector<const SMDS_MeshNode*>& nextNodes,
4230                       const int                           iNotSame)
4231 {
4232
4233   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4234   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4235   gp_XYZ extrDir( pN - pP ), faceNorm;
4236   SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4237
4238   return faceNorm * extrDir < 0.0;
4239 }
4240
4241 //=======================================================================
4242 /*!
4243  * \brief Create elements by sweeping an element
4244  * \param elem - element to sweep
4245  * \param newNodesItVec - nodes generated from each node of the element
4246  * \param newElems - generated elements
4247  * \param nbSteps - number of sweeping steps
4248  * \param srcElements - to append elem for each generated element
4249  */
4250 //=======================================================================
4251
4252 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4253                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4254                                     list<const SMDS_MeshElement*>&        newElems,
4255                                     const int                             nbSteps,
4256                                     SMESH_SequenceOfElemPtr&              srcElements)
4257 {
4258   //MESSAGE("sweepElement " << nbSteps);
4259   SMESHDS_Mesh* aMesh = GetMeshDS();
4260
4261   const int           nbNodes = elem->NbNodes();
4262   const int         nbCorners = elem->NbCornerNodes();
4263   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4264                                                           polyhedron creation !!! */
4265   // Loop on elem nodes:
4266   // find new nodes and detect same nodes indices
4267   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4268   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4269   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4270   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4271
4272   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4273   vector<int> sames(nbNodes);
4274   vector<bool> isSingleNode(nbNodes);
4275
4276   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4277     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4278     const SMDS_MeshNode*                         node = nnIt->first;
4279     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4280     if ( listNewNodes.empty() )
4281       return;
4282
4283     itNN   [ iNode ] = listNewNodes.begin();
4284     prevNod[ iNode ] = node;
4285     nextNod[ iNode ] = listNewNodes.front();
4286
4287     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4288                                                              corner node of linear */
4289     if ( prevNod[ iNode ] != nextNod [ iNode ])
4290       nbDouble += !isSingleNode[iNode];
4291
4292     if( iNode < nbCorners ) { // check corners only
4293       if ( prevNod[ iNode ] == nextNod [ iNode ])
4294         sames[nbSame++] = iNode;
4295       else
4296         iNotSameNode = iNode;
4297     }
4298   }
4299
4300   if ( nbSame == nbNodes || nbSame > 2) {
4301     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4302     return;
4303   }
4304
4305   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4306   {
4307     // fix nodes order to have bottom normal external
4308     if ( baseType == SMDSEntity_Polygon )
4309     {
4310       std::reverse( itNN.begin(), itNN.end() );
4311       std::reverse( prevNod.begin(), prevNod.end() );
4312       std::reverse( midlNod.begin(), midlNod.end() );
4313       std::reverse( nextNod.begin(), nextNod.end() );
4314       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4315     }
4316     else
4317     {
4318       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4319       SMDS_MeshCell::applyInterlace( ind, itNN );
4320       SMDS_MeshCell::applyInterlace( ind, prevNod );
4321       SMDS_MeshCell::applyInterlace( ind, nextNod );
4322       SMDS_MeshCell::applyInterlace( ind, midlNod );
4323       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4324       if ( nbSame > 0 )
4325       {
4326         sames[nbSame] = iNotSameNode;
4327         for ( int j = 0; j <= nbSame; ++j )
4328           for ( size_t i = 0; i < ind.size(); ++i )
4329             if ( ind[i] == sames[j] )
4330             {
4331               sames[j] = i;
4332               break;
4333             }
4334         iNotSameNode = sames[nbSame];
4335       }
4336     }
4337   }
4338   else if ( elem->GetType() == SMDSAbs_Edge )
4339   {
4340     // orient a new face same as adjacent one
4341     int i1, i2;
4342     const SMDS_MeshElement* e;
4343     TIDSortedElemSet dummy;
4344     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4345         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4346         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4347     {
4348       // there is an adjacent face, check order of nodes in it
4349       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4350       if ( sameOrder )
4351       {
4352         std::swap( itNN[0],    itNN[1] );
4353         std::swap( prevNod[0], prevNod[1] );
4354         std::swap( nextNod[0], nextNod[1] );
4355         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4356         if ( nbSame > 0 )
4357           sames[0] = 1 - sames[0];
4358         iNotSameNode = 1 - iNotSameNode;
4359       }
4360     }
4361   }
4362
4363   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4364   if ( nbSame > 0 ) {
4365     iSameNode    = sames[ nbSame-1 ];
4366     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4367     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4368     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4369   }
4370
4371   // make new elements
4372   for (int iStep = 0; iStep < nbSteps; iStep++ )
4373   {
4374     // get next nodes
4375     for ( iNode = 0; iNode < nbNodes; iNode++ )
4376     {
4377       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4378       nextNod[ iNode ] = *itNN[ iNode ]++;
4379     }
4380
4381     SMDS_MeshElement* aNewElem = 0;
4382     /*if(!elem->IsPoly())*/ {
4383       switch ( baseType ) {
4384       case SMDSEntity_0D:
4385       case SMDSEntity_Node: { // sweep NODE
4386         if ( nbSame == 0 ) {
4387           if ( isSingleNode[0] )
4388             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4389           else
4390             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4391         }
4392         else
4393           return;
4394         break;
4395       }
4396       case SMDSEntity_Edge: { // sweep EDGE
4397         if ( nbDouble == 0 )
4398         {
4399           if ( nbSame == 0 ) // ---> quadrangle
4400             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4401                                       nextNod[ 1 ], nextNod[ 0 ] );
4402           else               // ---> triangle
4403             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4404                                       nextNod[ iNotSameNode ] );
4405         }
4406         else                 // ---> polygon
4407         {
4408           vector<const SMDS_MeshNode*> poly_nodes;
4409           poly_nodes.push_back( prevNod[0] );
4410           poly_nodes.push_back( prevNod[1] );
4411           if ( prevNod[1] != nextNod[1] )
4412           {
4413             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4414             poly_nodes.push_back( nextNod[1] );
4415           }
4416           if ( prevNod[0] != nextNod[0] )
4417           {
4418             poly_nodes.push_back( nextNod[0] );
4419             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4420           }
4421           switch ( poly_nodes.size() ) {
4422           case 3:
4423             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4424             break;
4425           case 4:
4426             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4427                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4428             break;
4429           default:
4430             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4431           }
4432         }
4433         break;
4434       }
4435       case SMDSEntity_Triangle: // TRIANGLE --->
4436         {
4437           if ( nbDouble > 0 ) break;
4438           if ( nbSame == 0 )       // ---> pentahedron
4439             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4440                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4441
4442           else if ( nbSame == 1 )  // ---> pyramid
4443             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4444                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4445                                          nextNod[ iSameNode ]);
4446
4447           else // 2 same nodes:       ---> tetrahedron
4448             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4449                                          nextNod[ iNotSameNode ]);
4450           break;
4451         }
4452       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4453         {
4454           if ( nbSame == 2 )
4455             return;
4456           if ( nbDouble+nbSame == 2 )
4457           {
4458             if(nbSame==0) {      // ---> quadratic quadrangle
4459               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4460                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4461             }
4462             else { //(nbSame==1) // ---> quadratic triangle
4463               if(sames[0]==2) {
4464                 return; // medium node on axis
4465               }
4466               else if(sames[0]==0)
4467                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4468                                           prevNod[2], midlNod[1], nextNod[2] );
4469               else // sames[0]==1
4470                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4471                                           prevNod[2], nextNod[2], midlNod[0]);
4472             }
4473           }
4474           else if ( nbDouble == 3 )
4475           {
4476             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4477               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4478                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4479             }
4480           }
4481           else
4482             return;
4483           break;
4484         }
4485       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4486         if ( nbDouble > 0 ) break;
4487
4488         if ( nbSame == 0 )       // ---> hexahedron
4489           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4490                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4491
4492         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4493           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4494                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4495                                        nextNod[ iSameNode ]);
4496           newElems.push_back( aNewElem );
4497           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4498                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4499                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4500         }
4501         else if ( nbSame == 2 ) { // ---> pentahedron
4502           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4503             // iBeforeSame is same too
4504             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4505                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4506                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4507           else
4508             // iAfterSame is same too
4509             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4510                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4511                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4512         }
4513         break;
4514       }
4515       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4516       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4517         if ( nbDouble+nbSame != 3 ) break;
4518         if(nbSame==0) {
4519           // --->  pentahedron with 15 nodes
4520           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4521                                        nextNod[0], nextNod[1], nextNod[2],
4522                                        prevNod[3], prevNod[4], prevNod[5],
4523                                        nextNod[3], nextNod[4], nextNod[5],
4524                                        midlNod[0], midlNod[1], midlNod[2]);
4525         }
4526         else if(nbSame==1) {
4527           // --->  2d order pyramid of 13 nodes
4528           int apex = iSameNode;
4529           int i0 = ( apex + 1 ) % nbCorners;
4530           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4531           int i0a = apex + 3;
4532           int i1a = i1 + 3;
4533           int i01 = i0 + 3;
4534           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4535                                       nextNod[i0], nextNod[i1], prevNod[apex],
4536                                       prevNod[i01], midlNod[i0],
4537                                       nextNod[i01], midlNod[i1],
4538                                       prevNod[i1a], prevNod[i0a],
4539                                       nextNod[i0a], nextNod[i1a]);
4540         }
4541         else if(nbSame==2) {
4542           // --->  2d order tetrahedron of 10 nodes
4543           int n1 = iNotSameNode;
4544           int n2 = ( n1 + 1             ) % nbCorners;
4545           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4546           int n12 = n1 + 3;
4547           int n23 = n2 + 3;
4548           int n31 = n3 + 3;
4549           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4550                                        prevNod[n12], prevNod[n23], prevNod[n31],
4551                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4552         }
4553         break;
4554       }
4555       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4556         if( nbSame == 0 ) {
4557           if ( nbDouble != 4 ) break;
4558           // --->  hexahedron with 20 nodes
4559           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4560                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4561                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4562                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4563                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4564         }
4565         else if(nbSame==1) {
4566           // ---> pyramid + pentahedron - can not be created since it is needed
4567           // additional middle node at the center of face
4568           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4569           return;
4570         }
4571         else if( nbSame == 2 ) {
4572           if ( nbDouble != 2 ) break;
4573           // --->  2d order Pentahedron with 15 nodes
4574           int n1,n2,n4,n5;
4575           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4576             // iBeforeSame is same too
4577             n1 = iBeforeSame;
4578             n2 = iOpposSame;
4579             n4 = iSameNode;
4580             n5 = iAfterSame;
4581           }
4582           else {
4583             // iAfterSame is same too
4584             n1 = iSameNode;
4585             n2 = iBeforeSame;
4586             n4 = iAfterSame;
4587             n5 = iOpposSame;
4588           }
4589           int n12 = n2 + 4;
4590           int n45 = n4 + 4;
4591           int n14 = n1 + 4;
4592           int n25 = n5 + 4;
4593           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4594                                        prevNod[n4], prevNod[n5], nextNod[n5],
4595                                        prevNod[n12], midlNod[n2], nextNod[n12],
4596                                        prevNod[n45], midlNod[n5], nextNod[n45],
4597                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4598         }
4599         break;
4600       }
4601       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4602
4603         if( nbSame == 0 && nbDouble == 9 ) {
4604           // --->  tri-quadratic hexahedron with 27 nodes
4605           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4606                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4607                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4608                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4609                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4610                                        prevNod[8], // bottom center
4611                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4612                                        nextNod[8], // top center
4613                                        midlNod[8]);// elem center
4614         }
4615         else
4616         {
4617           return;
4618         }
4619         break;
4620       }
4621       case SMDSEntity_Polygon: { // sweep POLYGON
4622
4623         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4624           // --->  hexagonal prism
4625           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4626                                        prevNod[3], prevNod[4], prevNod[5],
4627                                        nextNod[0], nextNod[1], nextNod[2],
4628                                        nextNod[3], nextNod[4], nextNod[5]);
4629         }
4630         break;
4631       }
4632       case SMDSEntity_Ball:
4633         return;
4634
4635       default:
4636         break;
4637       } // switch ( baseType )
4638     } // scope
4639
4640     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4641     {
4642       if ( baseType != SMDSEntity_Polygon )
4643       {
4644         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4645         SMDS_MeshCell::applyInterlace( ind, prevNod );
4646         SMDS_MeshCell::applyInterlace( ind, nextNod );
4647         SMDS_MeshCell::applyInterlace( ind, midlNod );
4648         SMDS_MeshCell::applyInterlace( ind, itNN );
4649         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4650         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4651       }
4652       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4653       vector<int> quantities (nbNodes + 2);
4654       polyedre_nodes.clear();
4655       quantities.clear();
4656
4657       // bottom of prism
4658       for (int inode = 0; inode < nbNodes; inode++)
4659         polyedre_nodes.push_back( prevNod[inode] );
4660       quantities.push_back( nbNodes );
4661
4662       // top of prism
4663       polyedre_nodes.push_back( nextNod[0] );
4664       for (int inode = nbNodes; inode-1; --inode )
4665         polyedre_nodes.push_back( nextNod[inode-1] );
4666       quantities.push_back( nbNodes );
4667
4668       // side faces
4669       for (int iface = 0; iface < nbNodes; iface++)
4670       {
4671         const int prevNbNodes = polyedre_nodes.size();
4672         int inextface = (iface+1) % nbNodes;
4673         polyedre_nodes.push_back( prevNod[inextface] );
4674         polyedre_nodes.push_back( prevNod[iface] );
4675         if ( prevNod[iface] != nextNod[iface] )
4676         {
4677           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4678           polyedre_nodes.push_back( nextNod[iface] );
4679         }
4680         if ( prevNod[inextface] != nextNod[inextface] )
4681         {
4682           polyedre_nodes.push_back( nextNod[inextface] );
4683           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4684         }
4685         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4686         if ( nbFaceNodes > 2 )
4687           quantities.push_back( nbFaceNodes );
4688         else // degenerated face
4689           polyedre_nodes.resize( prevNbNodes );
4690       }
4691       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4692
4693     } //  // try to create a polyherdal prism
4694
4695     if ( aNewElem ) {
4696       newElems.push_back( aNewElem );
4697       myLastCreatedElems.Append(aNewElem);
4698       srcElements.Append( elem );
4699     }
4700
4701     // set new prev nodes
4702     for ( iNode = 0; iNode < nbNodes; iNode++ )
4703       prevNod[ iNode ] = nextNod[ iNode ];
4704
4705   } // loop on steps
4706 }
4707
4708 //=======================================================================
4709 /*!
4710  * \brief Create 1D and 2D elements around swept elements
4711  * \param mapNewNodes - source nodes and ones generated from them
4712  * \param newElemsMap - source elements and ones generated from them
4713  * \param elemNewNodesMap - nodes generated from each node of each element
4714  * \param elemSet - all swept elements
4715  * \param nbSteps - number of sweeping steps
4716  * \param srcElements - to append elem for each generated element
4717  */
4718 //=======================================================================
4719
4720 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4721                                   TTElemOfElemListMap &    newElemsMap,
4722                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4723                                   TIDSortedElemSet&        elemSet,
4724                                   const int                nbSteps,
4725                                   SMESH_SequenceOfElemPtr& srcElements)
4726 {
4727   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4728   SMESHDS_Mesh* aMesh = GetMeshDS();
4729
4730   // Find nodes belonging to only one initial element - sweep them into edges.
4731
4732   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4733   for ( ; nList != mapNewNodes.end(); nList++ )
4734   {
4735     const SMDS_MeshNode* node =
4736       static_cast<const SMDS_MeshNode*>( nList->first );
4737     if ( newElemsMap.count( node ))
4738       continue; // node was extruded into edge
4739     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4740     int nbInitElems = 0;
4741     const SMDS_MeshElement* el = 0;
4742     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4743     while ( eIt->more() && nbInitElems < 2 ) {
4744       el = eIt->next();
4745       SMDSAbs_ElementType type = el->GetType();
4746       if ( type == SMDSAbs_Volume || type < highType ) continue;
4747       if ( type > highType ) {
4748         nbInitElems = 0;
4749         highType = type;
4750       }
4751       nbInitElems += elemSet.count(el);
4752     }
4753     if ( nbInitElems < 2 ) {
4754       bool NotCreateEdge = el && el->IsMediumNode(node);
4755       if(!NotCreateEdge) {
4756         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4757         list<const SMDS_MeshElement*> newEdges;
4758         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4759       }
4760     }
4761   }
4762
4763   // Make a ceiling for each element ie an equal element of last new nodes.
4764   // Find free links of faces - make edges and sweep them into faces.
4765
4766   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4767   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4768   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4769   {
4770     const SMDS_MeshElement* elem = itElem->first;
4771     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4772
4773     if(itElem->second.size()==0) continue;
4774
4775     const bool isQuadratic = elem->IsQuadratic();
4776
4777     if ( elem->GetType() == SMDSAbs_Edge ) {
4778       // create a ceiling edge
4779       if ( !isQuadratic ) {
4780         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4781                                vecNewNodes[ 1 ]->second.back())) {
4782           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4783                                                    vecNewNodes[ 1 ]->second.back()));
4784           srcElements.Append( elem );
4785         }
4786       }
4787       else {
4788         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4789                                vecNewNodes[ 1 ]->second.back(),
4790                                vecNewNodes[ 2 ]->second.back())) {
4791           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4792                                                    vecNewNodes[ 1 ]->second.back(),
4793                                                    vecNewNodes[ 2 ]->second.back()));
4794           srcElements.Append( elem );
4795         }
4796       }
4797     }
4798     if ( elem->GetType() != SMDSAbs_Face )
4799       continue;
4800
4801     bool hasFreeLinks = false;
4802
4803     TIDSortedElemSet avoidSet;
4804     avoidSet.insert( elem );
4805
4806     set<const SMDS_MeshNode*> aFaceLastNodes;
4807     int iNode, nbNodes = vecNewNodes.size();
4808     if ( !isQuadratic ) {
4809       // loop on the face nodes
4810       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4811         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4812         // look for free links of the face
4813         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4814         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4815         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4816         // check if a link n1-n2 is free
4817         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4818           hasFreeLinks = true;
4819           // make a new edge and a ceiling for a new edge
4820           const SMDS_MeshElement* edge;
4821           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4822             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4823             srcElements.Append( myLastCreatedElems.Last() );
4824           }
4825           n1 = vecNewNodes[ iNode ]->second.back();
4826           n2 = vecNewNodes[ iNext ]->second.back();
4827           if ( !aMesh->FindEdge( n1, n2 )) {
4828             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4829             srcElements.Append( edge );
4830           }
4831         }
4832       }
4833     }
4834     else { // elem is quadratic face
4835       int nbn = nbNodes/2;
4836       for ( iNode = 0; iNode < nbn; iNode++ ) {
4837         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4838         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4839         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4840         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4841         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4842         // check if a link is free
4843         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4844              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4845              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4846           hasFreeLinks = true;
4847           // make an edge and a ceiling for a new edge
4848           // find medium node
4849           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4850             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4851             srcElements.Append( elem );
4852           }
4853           n1 = vecNewNodes[ iNode ]->second.back();
4854           n2 = vecNewNodes[ iNext ]->second.back();
4855           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4856           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4857             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4858             srcElements.Append( elem );
4859           }
4860         }
4861       }
4862       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4863         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4864       }
4865     }
4866
4867     // sweep free links into faces
4868
4869     if ( hasFreeLinks )  {
4870       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4871       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4872
4873       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4874       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4875       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4876         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4877         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4878       }
4879       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4880         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4881         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4882       }
4883       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4884         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4885         std::advance( v, volNb );
4886         // find indices of free faces of a volume and their source edges
4887         list< int > freeInd;
4888         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4889         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4890         int iF, nbF = vTool.NbFaces();
4891         for ( iF = 0; iF < nbF; iF ++ ) {
4892           if (vTool.IsFreeFace( iF ) &&
4893               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4894               initNodeSet != faceNodeSet) // except an initial face
4895           {
4896             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4897               continue;
4898             if ( faceNodeSet == initNodeSetNoCenter )
4899               continue;
4900             freeInd.push_back( iF );
4901             // find source edge of a free face iF
4902             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4903             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4904             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4905                                    initNodeSet.begin(), initNodeSet.end(),
4906                                    commonNodes.begin());
4907             if ( (*v)->IsQuadratic() )
4908               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4909             else
4910               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4911 #ifdef _DEBUG_
4912             if ( !srcEdges.back() )
4913             {
4914               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4915                    << iF << " of volume #" << vTool.ID() << endl;
4916             }
4917 #endif
4918           }
4919         }
4920         if ( freeInd.empty() )
4921           continue;
4922
4923         // create faces for all steps;
4924         // if such a face has been already created by sweep of edge,
4925         // assure that its orientation is OK
4926         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4927           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4928           vTool.SetExternalNormal();
4929           const int nextShift = vTool.IsForward() ? +1 : -1;
4930           list< int >::iterator ind = freeInd.begin();
4931           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4932           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4933           {
4934             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4935             int nbn = vTool.NbFaceNodes( *ind );
4936             const SMDS_MeshElement * f = 0;
4937             if ( nbn == 3 )              ///// triangle
4938             {
4939               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4940               if ( !f ||
4941                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4942               {
4943                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4944                                                      nodes[ 1 ],
4945                                                      nodes[ 1 + nextShift ] };
4946                 if ( f )
4947                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4948                 else
4949                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4950                                                             newOrder[ 2 ] ));
4951               }
4952             }
4953             else if ( nbn == 4 )       ///// quadrangle
4954             {
4955               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4956               if ( !f ||
4957                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4958               {
4959                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4960                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4961                 if ( f )
4962                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4963                 else
4964                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4965                                                             newOrder[ 2 ], newOrder[ 3 ]));
4966               }
4967             }
4968             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4969             {
4970               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4971               if ( !f ||
4972                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4973               {
4974                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4975                                                      nodes[2],
4976                                                      nodes[2 + 2*nextShift],
4977                                                      nodes[3 - 2*nextShift],
4978                                                      nodes[3],
4979                                                      nodes[3 + 2*nextShift]};
4980                 if ( f )
4981                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4982                 else
4983                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4984                                                             newOrder[ 1 ],
4985                                                             newOrder[ 2 ],
4986                                                             newOrder[ 3 ],
4987                                                             newOrder[ 4 ],
4988                                                             newOrder[ 5 ] ));
4989               }
4990             }
4991             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4992             {
4993               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4994                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4995               if ( !f ||
4996                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4997               {
4998                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4999                                                      nodes[4 - 2*nextShift],
5000                                                      nodes[4],
5001                                                      nodes[4 + 2*nextShift],
5002                                                      nodes[1],
5003                                                      nodes[5 - 2*nextShift],
5004                                                      nodes[5],
5005                                                      nodes[5 + 2*nextShift] };
5006                 if ( f )
5007                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5008                 else
5009                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5010                                                            newOrder[ 2 ], newOrder[ 3 ],
5011                                                            newOrder[ 4 ], newOrder[ 5 ],
5012                                                            newOrder[ 6 ], newOrder[ 7 ]));
5013               }
5014             }
5015             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5016             {
5017               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5018                                       SMDSAbs_Face, /*noMedium=*/false);
5019               if ( !f ||
5020                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5021               {
5022                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5023                                                      nodes[4 - 2*nextShift],
5024                                                      nodes[4],
5025                                                      nodes[4 + 2*nextShift],
5026                                                      nodes[1],
5027                                                      nodes[5 - 2*nextShift],
5028                                                      nodes[5],
5029                                                      nodes[5 + 2*nextShift],
5030                                                      nodes[8] };
5031                 if ( f )
5032                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5033                 else
5034                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5035                                                            newOrder[ 2 ], newOrder[ 3 ],
5036                                                            newOrder[ 4 ], newOrder[ 5 ],
5037                                                            newOrder[ 6 ], newOrder[ 7 ],
5038                                                            newOrder[ 8 ]));
5039               }
5040             }
5041             else  //////// polygon
5042             {
5043               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5044               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5045               if ( !f ||
5046                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5047               {
5048                 if ( !vTool.IsForward() )
5049                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5050                 if ( f )
5051                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5052                 else
5053                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5054               }
5055             }
5056
5057             while ( srcElements.Length() < myLastCreatedElems.Length() )
5058               srcElements.Append( *srcEdge );
5059
5060           }  // loop on free faces
5061
5062           // go to the next volume
5063           iVol = 0;
5064           while ( iVol++ < nbVolumesByStep ) v++;
5065
5066         } // loop on steps
5067       } // loop on volumes of one step
5068     } // sweep free links into faces
5069
5070     // Make a ceiling face with a normal external to a volume
5071
5072     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5073     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5074     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5075
5076     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5077       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5078       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5079     }
5080     if ( iF >= 0 ) {
5081       lastVol.SetExternalNormal();
5082       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5083       int nbn = lastVol.NbFaceNodes( iF );
5084       // we do not use this->AddElement() because nodes are interlaced
5085       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5086       if ( !hasFreeLinks ||
5087            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5088       {
5089         if ( nbn == 3 )
5090           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5091
5092         else if ( nbn == 4 )
5093           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5094
5095         else if ( nbn == 6 && isQuadratic )
5096           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5097                                                     nodes[1], nodes[3], nodes[5]));
5098         else if ( nbn == 7 && isQuadratic )
5099           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5100                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
5101         else if ( nbn == 8 && isQuadratic )
5102           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5103                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5104         else if ( nbn == 9 && isQuadratic )
5105           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5106                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5107                                                     nodes[8]));
5108         else
5109           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5110
5111         while ( srcElements.Length() < myLastCreatedElems.Length() )
5112           srcElements.Append( elem );
5113       }
5114     }
5115   } // loop on swept elements
5116 }
5117
5118 //=======================================================================
5119 //function : RotationSweep
5120 //purpose  :
5121 //=======================================================================
5122
5123 SMESH_MeshEditor::PGroupIDs
5124 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5125                                 const gp_Ax1&      theAxis,
5126                                 const double       theAngle,
5127                                 const int          theNbSteps,
5128                                 const double       theTol,
5129                                 const bool         theMakeGroups,
5130                                 const bool         theMakeWalls)
5131 {
5132   myLastCreatedElems.Clear();
5133   myLastCreatedNodes.Clear();
5134
5135   // source elements for each generated one
5136   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5137
5138   MESSAGE( "RotationSweep()");
5139   gp_Trsf aTrsf;
5140   aTrsf.SetRotation( theAxis, theAngle );
5141   gp_Trsf aTrsf2;
5142   aTrsf2.SetRotation( theAxis, theAngle/2. );
5143
5144   gp_Lin aLine( theAxis );
5145   double aSqTol = theTol * theTol;
5146
5147   SMESHDS_Mesh* aMesh = GetMeshDS();
5148
5149   TNodeOfNodeListMap mapNewNodes;
5150   TElemOfVecOfNnlmiMap mapElemNewNodes;
5151   TTElemOfElemListMap newElemsMap;
5152
5153   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5154                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5155                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5156   // loop on theElems
5157   TIDSortedElemSet::iterator itElem;
5158   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5159     const SMDS_MeshElement* elem = *itElem;
5160     if ( !elem || elem->GetType() == SMDSAbs_Volume )
5161       continue;
5162     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5163     newNodesItVec.reserve( elem->NbNodes() );
5164
5165     // loop on elem nodes
5166     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5167     while ( itN->more() )
5168     {
5169       // check if a node has been already sweeped
5170       const SMDS_MeshNode* node = cast2Node( itN->next() );
5171
5172       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5173       double coord[3];
5174       aXYZ.Coord( coord[0], coord[1], coord[2] );
5175       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5176
5177       TNodeOfNodeListMapItr nIt =
5178         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5179       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5180       if ( listNewNodes.empty() )
5181       {
5182         // check if we are to create medium nodes between corner ones
5183         bool needMediumNodes = false;
5184         if ( isQuadraticMesh )
5185         {
5186           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5187           while (it->more() && !needMediumNodes )
5188           {
5189             const SMDS_MeshElement* invElem = it->next();
5190             if ( invElem != elem && !theElems.count( invElem )) continue;
5191             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5192             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5193               needMediumNodes = true;
5194           }
5195         }
5196
5197         // make new nodes
5198         const SMDS_MeshNode * newNode = node;
5199         for ( int i = 0; i < theNbSteps; i++ ) {
5200           if ( !isOnAxis ) {
5201             if ( needMediumNodes )  // create a medium node
5202             {
5203               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5204               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5205               myLastCreatedNodes.Append(newNode);
5206               srcNodes.Append( node );
5207               listNewNodes.push_back( newNode );
5208               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5209             }
5210             else {
5211               aTrsf.Transforms( coord[0], coord[1], coord[2] );
5212             }
5213             // create a corner node
5214             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5215             myLastCreatedNodes.Append(newNode);
5216             srcNodes.Append( node );
5217             listNewNodes.push_back( newNode );
5218           }
5219           else {
5220             listNewNodes.push_back( newNode );
5221             // if ( needMediumNodes )
5222             //   listNewNodes.push_back( newNode );
5223           }
5224         }
5225       }
5226       newNodesItVec.push_back( nIt );
5227     }
5228     // make new elements
5229     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5230   }
5231
5232   if ( theMakeWalls )
5233     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5234
5235   PGroupIDs newGroupIDs;
5236   if ( theMakeGroups )
5237     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5238
5239   return newGroupIDs;
5240 }
5241
5242 //=======================================================================
5243 //function : ExtrusParam
5244 //purpose  : standard construction
5245 //=======================================================================
5246
5247 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5248                                             const int      theNbSteps,
5249                                             const int      theFlags,
5250                                             const double   theTolerance):
5251   myDir( theStep ),
5252   myFlags( theFlags ),
5253   myTolerance( theTolerance ),
5254   myElemsToUse( NULL )
5255 {
5256   mySteps = new TColStd_HSequenceOfReal;
5257   const double stepSize = theStep.Magnitude();
5258   for (int i=1; i<=theNbSteps; i++ )
5259     mySteps->Append( stepSize );
5260
5261   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5262       ( theTolerance > 0 ))
5263   {
5264     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5265   }
5266   else
5267   {
5268     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5269   }
5270 }
5271
5272 //=======================================================================
5273 //function : ExtrusParam
5274 //purpose  : steps are given explicitly
5275 //=======================================================================
5276
5277 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5278                                             Handle(TColStd_HSequenceOfReal) theSteps,
5279                                             const int                       theFlags,
5280                                             const double                    theTolerance):
5281   myDir( theDir ),
5282   mySteps( theSteps ),
5283   myFlags( theFlags ),
5284   myTolerance( theTolerance ),
5285   myElemsToUse( NULL )
5286 {
5287   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5288       ( theTolerance > 0 ))
5289   {
5290     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5291   }
5292   else
5293   {
5294     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5295   }
5296 }
5297
5298 //=======================================================================
5299 //function : ExtrusParam
5300 //purpose  : for extrusion by normal
5301 //=======================================================================
5302
5303 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5304                                             const int    theNbSteps,
5305                                             const int    theFlags,
5306                                             const int    theDim ):
5307   myDir( 1,0,0 ),
5308   mySteps( new TColStd_HSequenceOfReal ),
5309   myFlags( theFlags ),
5310   myTolerance( 0 ),
5311   myElemsToUse( NULL )
5312 {
5313   for (int i = 0; i < theNbSteps; i++ )
5314     mySteps->Append( theStepSize );
5315
5316   if ( theDim == 1 )
5317   {
5318     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5319   }
5320   else
5321   {
5322     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5323   }
5324 }
5325
5326 //=======================================================================
5327 //function : ExtrusParam::SetElementsToUse
5328 //purpose  : stores elements to use for extrusion by normal, depending on
5329 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5330 //=======================================================================
5331
5332 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5333 {
5334   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5335 }
5336
5337 //=======================================================================
5338 //function : ExtrusParam::beginStepIter
5339 //purpose  : prepare iteration on steps
5340 //=======================================================================
5341
5342 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5343 {
5344   myWithMediumNodes = withMediumNodes;
5345   myNextStep = 1;
5346   myCurSteps.clear();
5347 }
5348 //=======================================================================
5349 //function : ExtrusParam::moreSteps
5350 //purpose  : are there more steps?
5351 //=======================================================================
5352
5353 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5354 {
5355   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5356 }
5357 //=======================================================================
5358 //function : ExtrusParam::nextStep
5359 //purpose  : returns the next step
5360 //=======================================================================
5361
5362 double SMESH_MeshEditor::ExtrusParam::nextStep()
5363 {
5364   double res = 0;
5365   if ( !myCurSteps.empty() )
5366   {
5367     res = myCurSteps.back();
5368     myCurSteps.pop_back();
5369   }
5370   else if ( myNextStep <= mySteps->Length() )
5371   {
5372     myCurSteps.push_back( mySteps->Value( myNextStep ));
5373     ++myNextStep;
5374     if ( myWithMediumNodes )
5375     {
5376       myCurSteps.back() /= 2.;
5377       myCurSteps.push_back( myCurSteps.back() );
5378     }
5379     res = nextStep();
5380   }
5381   return res;
5382 }
5383
5384 //=======================================================================
5385 //function : ExtrusParam::makeNodesByDir
5386 //purpose  : create nodes for standard extrusion
5387 //=======================================================================
5388
5389 int SMESH_MeshEditor::ExtrusParam::
5390 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5391                 const SMDS_MeshNode*              srcNode,
5392                 std::list<const SMDS_MeshNode*> & newNodes,
5393                 const bool                        makeMediumNodes)
5394 {
5395   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5396
5397   int nbNodes = 0;
5398   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5399   {
5400     p += myDir.XYZ() * nextStep();
5401     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5402     newNodes.push_back( newNode );
5403   }
5404   return nbNodes;
5405 }
5406
5407 //=======================================================================
5408 //function : ExtrusParam::makeNodesByDirAndSew
5409 //purpose  : create nodes for standard extrusion with sewing
5410 //=======================================================================
5411
5412 int SMESH_MeshEditor::ExtrusParam::
5413 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5414                       const SMDS_MeshNode*              srcNode,
5415                       std::list<const SMDS_MeshNode*> & newNodes,
5416                       const bool                        makeMediumNodes)
5417 {
5418   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5419
5420   int nbNodes = 0;
5421   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5422   {
5423     P1 += myDir.XYZ() * nextStep();
5424
5425     // try to search in sequence of existing nodes
5426     // if myNodes.Length()>0 we 'nave to use given sequence
5427     // else - use all nodes of mesh
5428     const SMDS_MeshNode * node = 0;
5429     if ( myNodes.Length() > 0 ) {
5430       int i;
5431       for(i=1; i<=myNodes.Length(); i++) {
5432         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5433         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5434         {
5435           node = myNodes.Value(i);
5436           break;
5437         }
5438       }
5439     }
5440     else {
5441       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5442       while(itn->more()) {
5443         SMESH_TNodeXYZ P2( itn->next() );
5444         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5445         {
5446           node = P2._node;
5447           break;
5448         }
5449       }
5450     }
5451
5452     if ( !node )
5453       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5454
5455     newNodes.push_back( node );
5456
5457   } // loop on steps
5458
5459   return nbNodes;
5460 }
5461
5462 //=======================================================================
5463 //function : ExtrusParam::makeNodesByNormal2D
5464 //purpose  : create nodes for extrusion using normals of faces
5465 //=======================================================================
5466
5467 int SMESH_MeshEditor::ExtrusParam::
5468 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5469                      const SMDS_MeshNode*              srcNode,
5470                      std::list<const SMDS_MeshNode*> & newNodes,
5471                      const bool                        makeMediumNodes)
5472 {
5473   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5474
5475   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5476
5477   // get normals to faces sharing srcNode
5478   vector< gp_XYZ > norms, baryCenters;
5479   gp_XYZ norm, avgNorm( 0,0,0 );
5480   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5481   while ( faceIt->more() )
5482   {
5483     const SMDS_MeshElement* face = faceIt->next();
5484     if ( myElemsToUse && !myElemsToUse->count( face ))
5485       continue;
5486     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5487     {
5488       norms.push_back( norm );
5489       avgNorm += norm;
5490       if ( !alongAvgNorm )
5491       {
5492         gp_XYZ bc(0,0,0);
5493         int nbN = 0;
5494         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5495           bc += SMESH_TNodeXYZ( nIt->next() );
5496         baryCenters.push_back( bc / nbN );
5497       }
5498     }
5499   }
5500
5501   if ( norms.empty() ) return 0;
5502
5503   double normSize = avgNorm.Modulus();
5504   if ( normSize < std::numeric_limits<double>::min() )
5505     return 0;
5506
5507   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5508   {
5509     myDir = avgNorm;
5510     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5511   }
5512
5513   avgNorm /= normSize;
5514
5515   int nbNodes = 0;
5516   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5517   {
5518     gp_XYZ pNew = p;
5519     double stepSize = nextStep();
5520
5521     if ( norms.size() > 1 )
5522     {
5523       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5524       {
5525         // translate plane of a face
5526         baryCenters[ iF ] += norms[ iF ] * stepSize;
5527
5528         // find point of intersection of the face plane located at baryCenters[ iF ]
5529         // and avgNorm located at pNew
5530         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5531         double dot  = ( norms[ iF ] * avgNorm );
5532         if ( dot < std::numeric_limits<double>::min() )
5533           dot = stepSize * 1e-3;
5534         double step = -( norms[ iF ] * pNew + d ) / dot;
5535         pNew += step * avgNorm;
5536       }
5537     }
5538     else
5539     {
5540       pNew += stepSize * avgNorm;
5541     }
5542     p = pNew;
5543
5544     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5545     newNodes.push_back( newNode );
5546   }
5547   return nbNodes;
5548 }
5549
5550 //=======================================================================
5551 //function : ExtrusParam::makeNodesByNormal1D
5552 //purpose  : create nodes for extrusion using normals of edges
5553 //=======================================================================
5554
5555 int SMESH_MeshEditor::ExtrusParam::
5556 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5557                      const SMDS_MeshNode*              srcNode,
5558                      std::list<const SMDS_MeshNode*> & newNodes,
5559                      const bool                        makeMediumNodes)
5560 {
5561   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5562   return 0;
5563 }
5564
5565 //=======================================================================
5566 //function : ExtrusionSweep
5567 //purpose  :
5568 //=======================================================================
5569
5570 SMESH_MeshEditor::PGroupIDs
5571 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5572                                   const gp_Vec&        theStep,
5573                                   const int            theNbSteps,
5574                                   TTElemOfElemListMap& newElemsMap,
5575                                   const int            theFlags,
5576                                   const double         theTolerance)
5577 {
5578   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5579   return ExtrusionSweep( theElems, aParams, newElemsMap );
5580 }
5581
5582
5583 //=======================================================================
5584 //function : ExtrusionSweep
5585 //purpose  :
5586 //=======================================================================
5587
5588 SMESH_MeshEditor::PGroupIDs
5589 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5590                                   ExtrusParam&         theParams,
5591                                   TTElemOfElemListMap& newElemsMap)
5592 {
5593   myLastCreatedElems.Clear();
5594   myLastCreatedNodes.Clear();
5595
5596   // source elements for each generated one
5597   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5598
5599   SMESHDS_Mesh* aMesh = GetMeshDS();
5600
5601   const int nbSteps = theParams.NbSteps();
5602   theParams.SetElementsToUse( theElems );
5603
5604   TNodeOfNodeListMap mapNewNodes;
5605   //TNodeOfNodeVecMap mapNewNodes;
5606   TElemOfVecOfNnlmiMap mapElemNewNodes;
5607   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5608
5609   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5610                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5611                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5612   // loop on theElems
5613   TIDSortedElemSet::iterator itElem;
5614   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5615   {
5616     // check element type
5617     const SMDS_MeshElement* elem = *itElem;
5618     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5619       continue;
5620
5621     const size_t nbNodes = elem->NbNodes();
5622     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5623     newNodesItVec.reserve( nbNodes );
5624
5625     // loop on elem nodes
5626     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5627     while ( itN->more() )
5628     {
5629       // check if a node has been already sweeped
5630       const SMDS_MeshNode* node = cast2Node( itN->next() );
5631       TNodeOfNodeListMap::iterator nIt =
5632         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5633       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5634       if ( listNewNodes.empty() )
5635       {
5636         // make new nodes
5637
5638         // check if we are to create medium nodes between corner ones
5639         bool needMediumNodes = false;
5640         if ( isQuadraticMesh )
5641         {
5642           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5643           while (it->more() && !needMediumNodes )
5644           {
5645             const SMDS_MeshElement* invElem = it->next();
5646             if ( invElem != elem && !theElems.count( invElem )) continue;
5647             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5648             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5649               needMediumNodes = true;
5650           }
5651         }
5652         // create nodes for all steps
5653         if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5654         {
5655           list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5656           for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5657           {
5658             myLastCreatedNodes.Append( *newNodesIt );
5659             srcNodes.Append( node );
5660           }
5661         }
5662         else
5663         {
5664           break; // newNodesItVec will be shorter than nbNodes
5665         }
5666       }
5667       newNodesItVec.push_back( nIt );
5668     }
5669     // make new elements
5670     if ( newNodesItVec.size() == nbNodes )
5671       sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5672   }
5673
5674   if ( theParams.ToMakeBoundary() ) {
5675     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbSteps, srcElems );
5676   }
5677   PGroupIDs newGroupIDs;
5678   if ( theParams.ToMakeGroups() )
5679     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5680
5681   return newGroupIDs;
5682 }
5683
5684 //=======================================================================
5685 //function : ExtrusionAlongTrack
5686 //purpose  :
5687 //=======================================================================
5688 SMESH_MeshEditor::Extrusion_Error
5689 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5690                                        SMESH_subMesh*       theTrack,
5691                                        const SMDS_MeshNode* theN1,
5692                                        const bool           theHasAngles,
5693                                        list<double>&        theAngles,
5694                                        const bool           theLinearVariation,
5695                                        const bool           theHasRefPoint,
5696                                        const gp_Pnt&        theRefPoint,
5697                                        const bool           theMakeGroups)
5698 {
5699   MESSAGE("ExtrusionAlongTrack");
5700   myLastCreatedElems.Clear();
5701   myLastCreatedNodes.Clear();
5702
5703   int aNbE;
5704   std::list<double> aPrms;
5705   TIDSortedElemSet::iterator itElem;
5706
5707   gp_XYZ aGC;
5708   TopoDS_Edge aTrackEdge;
5709   TopoDS_Vertex aV1, aV2;
5710
5711   SMDS_ElemIteratorPtr aItE;
5712   SMDS_NodeIteratorPtr aItN;
5713   SMDSAbs_ElementType aTypeE;
5714
5715   TNodeOfNodeListMap mapNewNodes;
5716
5717   // 1. Check data
5718   aNbE = theElements.size();
5719   // nothing to do
5720   if ( !aNbE )
5721     return EXTR_NO_ELEMENTS;
5722
5723   // 1.1 Track Pattern
5724   ASSERT( theTrack );
5725
5726   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5727
5728   aItE = pSubMeshDS->GetElements();
5729   while ( aItE->more() ) {
5730     const SMDS_MeshElement* pE = aItE->next();
5731     aTypeE = pE->GetType();
5732     // Pattern must contain links only
5733     if ( aTypeE != SMDSAbs_Edge )
5734       return EXTR_PATH_NOT_EDGE;
5735   }
5736
5737   list<SMESH_MeshEditor_PathPoint> fullList;
5738
5739   const TopoDS_Shape& aS = theTrack->GetSubShape();
5740   // Sub-shape for the Pattern must be an Edge or Wire
5741   if( aS.ShapeType() == TopAbs_EDGE ) {
5742     aTrackEdge = TopoDS::Edge( aS );
5743     // the Edge must not be degenerated
5744     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5745       return EXTR_BAD_PATH_SHAPE;
5746     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5747     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5748     const SMDS_MeshNode* aN1 = aItN->next();
5749     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5750     const SMDS_MeshNode* aN2 = aItN->next();
5751     // starting node must be aN1 or aN2
5752     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5753       return EXTR_BAD_STARTING_NODE;
5754     aItN = pSubMeshDS->GetNodes();
5755     while ( aItN->more() ) {
5756       const SMDS_MeshNode* pNode = aItN->next();
5757       const SMDS_EdgePosition* pEPos =
5758         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5759       double aT = pEPos->GetUParameter();
5760       aPrms.push_back( aT );
5761     }
5762     //Extrusion_Error err =
5763     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5764   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5765     list< SMESH_subMesh* > LSM;
5766     TopTools_SequenceOfShape Edges;
5767     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5768     while(itSM->more()) {
5769       SMESH_subMesh* SM = itSM->next();
5770       LSM.push_back(SM);
5771       const TopoDS_Shape& aS = SM->GetSubShape();
5772       Edges.Append(aS);
5773     }
5774     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5775     int startNid = theN1->GetID();
5776     TColStd_MapOfInteger UsedNums;
5777
5778     int NbEdges = Edges.Length();
5779     int i = 1;
5780     for(; i<=NbEdges; i++) {
5781       int k = 0;
5782       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5783       for(; itLSM!=LSM.end(); itLSM++) {
5784         k++;
5785         if(UsedNums.Contains(k)) continue;
5786         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5787         SMESH_subMesh* locTrack = *itLSM;
5788         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5789         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5790         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5791         const SMDS_MeshNode* aN1 = aItN->next();
5792         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5793         const SMDS_MeshNode* aN2 = aItN->next();
5794         // starting node must be aN1 or aN2
5795         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5796         // 2. Collect parameters on the track edge
5797         aPrms.clear();
5798         aItN = locMeshDS->GetNodes();
5799         while ( aItN->more() ) {
5800           const SMDS_MeshNode* pNode = aItN->next();
5801           const SMDS_EdgePosition* pEPos =
5802             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5803           double aT = pEPos->GetUParameter();
5804           aPrms.push_back( aT );
5805         }
5806         list<SMESH_MeshEditor_PathPoint> LPP;
5807         //Extrusion_Error err =
5808         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5809         LLPPs.push_back(LPP);
5810         UsedNums.Add(k);
5811         // update startN for search following egde
5812         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5813         else startNid = aN1->GetID();
5814         break;
5815       }
5816     }
5817     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5818     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5819     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5820     for(; itPP!=firstList.end(); itPP++) {
5821       fullList.push_back( *itPP );
5822     }
5823     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5824     fullList.pop_back();
5825     itLLPP++;
5826     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5827       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5828       itPP = currList.begin();
5829       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5830       gp_Dir D1 = PP1.Tangent();
5831       gp_Dir D2 = PP2.Tangent();
5832       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5833                            (D1.Z()+D2.Z())/2 ) );
5834       PP1.SetTangent(Dnew);
5835       fullList.push_back(PP1);
5836       itPP++;
5837       for(; itPP!=firstList.end(); itPP++) {
5838         fullList.push_back( *itPP );
5839       }
5840       PP1 = fullList.back();
5841       fullList.pop_back();
5842     }
5843     // if wire not closed
5844     fullList.push_back(PP1);
5845     // else ???
5846   }
5847   else {
5848     return EXTR_BAD_PATH_SHAPE;
5849   }
5850
5851   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5852                           theHasRefPoint, theRefPoint, theMakeGroups);
5853 }
5854
5855
5856 //=======================================================================
5857 //function : ExtrusionAlongTrack
5858 //purpose  :
5859 //=======================================================================
5860 SMESH_MeshEditor::Extrusion_Error
5861 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5862                                        SMESH_Mesh*          theTrack,
5863                                        const SMDS_MeshNode* theN1,
5864                                        const bool           theHasAngles,
5865                                        list<double>&        theAngles,
5866                                        const bool           theLinearVariation,
5867                                        const bool           theHasRefPoint,
5868                                        const gp_Pnt&        theRefPoint,
5869                                        const bool           theMakeGroups)
5870 {
5871   myLastCreatedElems.Clear();
5872   myLastCreatedNodes.Clear();
5873
5874   int aNbE;
5875   std::list<double> aPrms;
5876   TIDSortedElemSet::iterator itElem;
5877
5878   gp_XYZ aGC;
5879   TopoDS_Edge aTrackEdge;
5880   TopoDS_Vertex aV1, aV2;
5881
5882   SMDS_ElemIteratorPtr aItE;
5883   SMDS_NodeIteratorPtr aItN;
5884   SMDSAbs_ElementType aTypeE;
5885
5886   TNodeOfNodeListMap mapNewNodes;
5887
5888   // 1. Check data
5889   aNbE = theElements.size();
5890   // nothing to do
5891   if ( !aNbE )
5892     return EXTR_NO_ELEMENTS;
5893
5894   // 1.1 Track Pattern
5895   ASSERT( theTrack );
5896
5897   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5898
5899   aItE = pMeshDS->elementsIterator();
5900   while ( aItE->more() ) {
5901     const SMDS_MeshElement* pE = aItE->next();
5902     aTypeE = pE->GetType();
5903     // Pattern must contain links only
5904     if ( aTypeE != SMDSAbs_Edge )
5905       return EXTR_PATH_NOT_EDGE;
5906   }
5907
5908   list<SMESH_MeshEditor_PathPoint> fullList;
5909
5910   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5911
5912   if ( !theTrack->HasShapeToMesh() ) {
5913     //Mesh without shape
5914     const SMDS_MeshNode* currentNode = NULL;
5915     const SMDS_MeshNode* prevNode = theN1;
5916     std::vector<const SMDS_MeshNode*> aNodesList;
5917     aNodesList.push_back(theN1);
5918     int nbEdges = 0, conn=0;
5919     const SMDS_MeshElement* prevElem = NULL;
5920     const SMDS_MeshElement* currentElem = NULL;
5921     int totalNbEdges = theTrack->NbEdges();
5922     SMDS_ElemIteratorPtr nIt;
5923
5924     //check start node
5925     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5926       return EXTR_BAD_STARTING_NODE;
5927     }
5928
5929     conn = nbEdgeConnectivity(theN1);
5930     if(conn > 2)
5931       return EXTR_PATH_NOT_EDGE;
5932
5933     aItE = theN1->GetInverseElementIterator();
5934     prevElem = aItE->next();
5935     currentElem = prevElem;
5936     //Get all nodes
5937     if(totalNbEdges == 1 ) {
5938       nIt = currentElem->nodesIterator();
5939       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5940       if(currentNode == prevNode)
5941         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5942       aNodesList.push_back(currentNode);
5943     } else {
5944       nIt = currentElem->nodesIterator();
5945       while( nIt->more() ) {
5946         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5947         if(currentNode == prevNode)
5948           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5949         aNodesList.push_back(currentNode);
5950
5951         //case of the closed mesh
5952         if(currentNode == theN1) {
5953           nbEdges++;
5954           break;
5955         }
5956
5957         conn = nbEdgeConnectivity(currentNode);
5958         if(conn > 2) {
5959           return EXTR_PATH_NOT_EDGE;
5960         }else if( conn == 1 && nbEdges > 0 ) {
5961           //End of the path
5962           nbEdges++;
5963           break;
5964         }else {
5965           prevNode = currentNode;
5966           aItE = currentNode->GetInverseElementIterator();
5967           currentElem = aItE->next();
5968           if( currentElem  == prevElem)
5969             currentElem = aItE->next();
5970           nIt = currentElem->nodesIterator();
5971           prevElem = currentElem;
5972           nbEdges++;
5973         }
5974       }
5975     }
5976
5977     if(nbEdges != totalNbEdges)
5978       return EXTR_PATH_NOT_EDGE;
5979
5980     TopTools_SequenceOfShape Edges;
5981     double x1,x2,y1,y2,z1,z2;
5982     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5983     int startNid = theN1->GetID();
5984     for(int i = 1; i < aNodesList.size(); i++) {
5985       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5986       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5987       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5988       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5989       list<SMESH_MeshEditor_PathPoint> LPP;
5990       aPrms.clear();
5991       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5992       LLPPs.push_back(LPP);
5993       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5994       else startNid = aNodesList[i-1]->GetID();
5995
5996     }
5997
5998     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5999     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6000     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6001     for(; itPP!=firstList.end(); itPP++) {
6002       fullList.push_back( *itPP );
6003     }
6004
6005     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6006     SMESH_MeshEditor_PathPoint PP2;
6007     fullList.pop_back();
6008     itLLPP++;
6009     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6010       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6011       itPP = currList.begin();
6012       PP2 = currList.front();
6013       gp_Dir D1 = PP1.Tangent();
6014       gp_Dir D2 = PP2.Tangent();
6015       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6016                            (D1.Z()+D2.Z())/2 ) );
6017       PP1.SetTangent(Dnew);
6018       fullList.push_back(PP1);
6019       itPP++;
6020       for(; itPP!=currList.end(); itPP++) {
6021         fullList.push_back( *itPP );
6022       }
6023       PP1 = fullList.back();
6024       fullList.pop_back();
6025     }
6026     fullList.push_back(PP1);
6027
6028   } // Sub-shape for the Pattern must be an Edge or Wire
6029   else if( aS.ShapeType() == TopAbs_EDGE ) {
6030     aTrackEdge = TopoDS::Edge( aS );
6031     // the Edge must not be degenerated
6032     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6033       return EXTR_BAD_PATH_SHAPE;
6034     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6035     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6036     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6037     // starting node must be aN1 or aN2
6038     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6039       return EXTR_BAD_STARTING_NODE;
6040     aItN = pMeshDS->nodesIterator();
6041     while ( aItN->more() ) {
6042       const SMDS_MeshNode* pNode = aItN->next();
6043       if( pNode==aN1 || pNode==aN2 ) continue;
6044       const SMDS_EdgePosition* pEPos =
6045         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6046       double aT = pEPos->GetUParameter();
6047       aPrms.push_back( aT );
6048     }
6049     //Extrusion_Error err =
6050     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6051   }
6052   else if( aS.ShapeType() == TopAbs_WIRE ) {
6053     list< SMESH_subMesh* > LSM;
6054     TopTools_SequenceOfShape Edges;
6055     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6056     for(; eExp.More(); eExp.Next()) {
6057       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6058       if( SMESH_Algo::isDegenerated(E) ) continue;
6059       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6060       if(SM) {
6061         LSM.push_back(SM);
6062         Edges.Append(E);
6063       }
6064     }
6065     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6066     TopoDS_Vertex aVprev;
6067     TColStd_MapOfInteger UsedNums;
6068     int NbEdges = Edges.Length();
6069     int i = 1;
6070     for(; i<=NbEdges; i++) {
6071       int k = 0;
6072       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6073       for(; itLSM!=LSM.end(); itLSM++) {
6074         k++;
6075         if(UsedNums.Contains(k)) continue;
6076         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6077         SMESH_subMesh* locTrack = *itLSM;
6078         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6079         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6080         bool aN1isOK = false, aN2isOK = false;
6081         if ( aVprev.IsNull() ) {
6082           // if previous vertex is not yet defined, it means that we in the beginning of wire
6083           // and we have to find initial vertex corresponding to starting node theN1
6084           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6085           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6086           // starting node must be aN1 or aN2
6087           aN1isOK = ( aN1 && aN1 == theN1 );
6088           aN2isOK = ( aN2 && aN2 == theN1 );
6089         }
6090         else {
6091           // we have specified ending vertex of the previous edge on the previous iteration
6092           // and we have just to check that it corresponds to any vertex in current segment
6093           aN1isOK = aVprev.IsSame( aV1 );
6094           aN2isOK = aVprev.IsSame( aV2 );
6095         }
6096         if ( !aN1isOK && !aN2isOK ) continue;
6097         // 2. Collect parameters on the track edge
6098         aPrms.clear();
6099         aItN = locMeshDS->GetNodes();
6100         while ( aItN->more() ) {
6101           const SMDS_MeshNode*     pNode = aItN->next();
6102           const SMDS_EdgePosition* pEPos =
6103             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6104           double aT = pEPos->GetUParameter();
6105           aPrms.push_back( aT );
6106         }
6107         list<SMESH_MeshEditor_PathPoint> LPP;
6108         //Extrusion_Error err =
6109         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6110         LLPPs.push_back(LPP);
6111         UsedNums.Add(k);
6112         // update startN for search following egde
6113         if ( aN1isOK ) aVprev = aV2;
6114         else           aVprev = aV1;
6115         break;
6116       }
6117     }
6118     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6119     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6120     fullList.splice( fullList.end(), firstList );
6121
6122     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6123     fullList.pop_back();
6124     itLLPP++;
6125     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6126       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6127       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6128       gp_Dir D1 = PP1.Tangent();
6129       gp_Dir D2 = PP2.Tangent();
6130       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6131       PP1.SetTangent(Dnew);
6132       fullList.push_back(PP1);
6133       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6134       PP1 = fullList.back();
6135       fullList.pop_back();
6136     }
6137     // if wire not closed
6138     fullList.push_back(PP1);
6139     // else ???
6140   }
6141   else {
6142     return EXTR_BAD_PATH_SHAPE;
6143   }
6144
6145   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6146                           theHasRefPoint, theRefPoint, theMakeGroups);
6147 }
6148
6149
6150 //=======================================================================
6151 //function : MakeEdgePathPoints
6152 //purpose  : auxilary for ExtrusionAlongTrack
6153 //=======================================================================
6154 SMESH_MeshEditor::Extrusion_Error
6155 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6156                                      const TopoDS_Edge&                aTrackEdge,
6157                                      bool                              FirstIsStart,
6158                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6159 {
6160   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6161   aTolVec=1.e-7;
6162   aTolVec2=aTolVec*aTolVec;
6163   double aT1, aT2;
6164   TopoDS_Vertex aV1, aV2;
6165   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6166   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6167   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6168   // 2. Collect parameters on the track edge
6169   aPrms.push_front( aT1 );
6170   aPrms.push_back( aT2 );
6171   // sort parameters
6172   aPrms.sort();
6173   if( FirstIsStart ) {
6174     if ( aT1 > aT2 ) {
6175       aPrms.reverse();
6176     }
6177   }
6178   else {
6179     if ( aT2 > aT1 ) {
6180       aPrms.reverse();
6181     }
6182   }
6183   // 3. Path Points
6184   SMESH_MeshEditor_PathPoint aPP;
6185   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6186   std::list<double>::iterator aItD = aPrms.begin();
6187   for(; aItD != aPrms.end(); ++aItD) {
6188     double aT = *aItD;
6189     gp_Pnt aP3D;
6190     gp_Vec aVec;
6191     aC3D->D1( aT, aP3D, aVec );
6192     aL2 = aVec.SquareMagnitude();
6193     if ( aL2 < aTolVec2 )
6194       return EXTR_CANT_GET_TANGENT;
6195     gp_Dir aTgt( aVec );
6196     aPP.SetPnt( aP3D );
6197     aPP.SetTangent( aTgt );
6198     aPP.SetParameter( aT );
6199     LPP.push_back(aPP);
6200   }
6201   return EXTR_OK;
6202 }
6203
6204
6205 //=======================================================================
6206 //function : MakeExtrElements
6207 //purpose  : auxilary for ExtrusionAlongTrack
6208 //=======================================================================
6209 SMESH_MeshEditor::Extrusion_Error
6210 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&                 theElements,
6211                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6212                                    const bool                        theHasAngles,
6213                                    list<double>&                     theAngles,
6214                                    const bool                        theLinearVariation,
6215                                    const bool                        theHasRefPoint,
6216                                    const gp_Pnt&                     theRefPoint,
6217                                    const bool                        theMakeGroups)
6218 {
6219   const int aNbTP = fullList.size();
6220   // Angles
6221   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6222     LinearAngleVariation(aNbTP-1, theAngles);
6223   // fill vector of path points with angles
6224   vector<SMESH_MeshEditor_PathPoint> aPPs;
6225   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6226   list<double>::iterator                 itAngles = theAngles.begin();
6227   aPPs.push_back( *itPP++ );
6228   for( ; itPP != fullList.end(); itPP++) {
6229     aPPs.push_back( *itPP );
6230     if ( theHasAngles && itAngles != theAngles.end() )
6231       aPPs.back().SetAngle( *itAngles++ );
6232   }
6233
6234   TNodeOfNodeListMap   mapNewNodes;
6235   TElemOfVecOfNnlmiMap mapElemNewNodes;
6236   TTElemOfElemListMap  newElemsMap;
6237   TIDSortedElemSet::iterator itElem;
6238   // source elements for each generated one
6239   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6240
6241   // 3. Center of rotation aV0
6242   gp_Pnt aV0 = theRefPoint;
6243   if ( !theHasRefPoint )
6244   {
6245     gp_XYZ aGC( 0.,0.,0. );
6246     TIDSortedElemSet newNodes;
6247
6248     itElem = theElements.begin();
6249     for ( ; itElem != theElements.end(); itElem++ ) {
6250       const SMDS_MeshElement* elem = *itElem;
6251
6252       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6253       while ( itN->more() ) {
6254         const SMDS_MeshElement* node = itN->next();
6255         if ( newNodes.insert( node ).second )
6256           aGC += SMESH_TNodeXYZ( node );
6257       }
6258     }
6259     aGC /= newNodes.size();
6260     aV0.SetXYZ( aGC );
6261   } // if (!theHasRefPoint) {
6262
6263   // 4. Processing the elements
6264   SMESHDS_Mesh* aMesh = GetMeshDS();
6265
6266   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6267     // check element type
6268     const SMDS_MeshElement* elem = *itElem;
6269     SMDSAbs_ElementType   aTypeE = elem->GetType();
6270     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
6271       continue;
6272
6273     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6274     newNodesItVec.reserve( elem->NbNodes() );
6275
6276     // loop on elem nodes
6277     int nodeIndex = -1;
6278     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6279     while ( itN->more() )
6280     {
6281       ++nodeIndex;
6282       // check if a node has been already processed
6283       const SMDS_MeshNode* node =
6284         static_cast<const SMDS_MeshNode*>( itN->next() );
6285       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6286       if ( nIt == mapNewNodes.end() ) {
6287         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6288         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6289
6290         // make new nodes
6291         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6292         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6293         gp_Ax1 anAx1, anAxT1T0;
6294         gp_Dir aDT1x, aDT0x, aDT1T0;
6295
6296         aTolAng=1.e-4;
6297
6298         aV0x = aV0;
6299         aPN0 = SMESH_TNodeXYZ( node );
6300
6301         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6302         aP0x = aPP0.Pnt();
6303         aDT0x= aPP0.Tangent();
6304         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6305
6306         for ( int j = 1; j < aNbTP; ++j ) {
6307           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6308           aP1x     = aPP1.Pnt();
6309           aDT1x    = aPP1.Tangent();
6310           aAngle1x = aPP1.Angle();
6311
6312           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6313           // Translation
6314           gp_Vec aV01x( aP0x, aP1x );
6315           aTrsf.SetTranslation( aV01x );
6316
6317           // traslated point
6318           aV1x = aV0x.Transformed( aTrsf );
6319           aPN1 = aPN0.Transformed( aTrsf );
6320
6321           // rotation 1 [ T1,T0 ]
6322           aAngleT1T0=-aDT1x.Angle( aDT0x );
6323           if (fabs(aAngleT1T0) > aTolAng) {
6324             aDT1T0=aDT1x^aDT0x;
6325             anAxT1T0.SetLocation( aV1x );
6326             anAxT1T0.SetDirection( aDT1T0 );
6327             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6328
6329             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6330           }
6331
6332           // rotation 2
6333           if ( theHasAngles ) {
6334             anAx1.SetLocation( aV1x );
6335             anAx1.SetDirection( aDT1x );
6336             aTrsfRot.SetRotation( anAx1, aAngle1x );
6337
6338             aPN1 = aPN1.Transformed( aTrsfRot );
6339           }
6340
6341           // make new node
6342           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6343           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6344             // create additional node
6345             double x = ( aPN1.X() + aPN0.X() )/2.;
6346             double y = ( aPN1.Y() + aPN0.Y() )/2.;
6347             double z = ( aPN1.Z() + aPN0.Z() )/2.;
6348             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6349             myLastCreatedNodes.Append(newNode);
6350             srcNodes.Append( node );
6351             listNewNodes.push_back( newNode );
6352           }
6353           const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6354           myLastCreatedNodes.Append(newNode);
6355           srcNodes.Append( node );
6356           listNewNodes.push_back( newNode );
6357
6358           aPN0 = aPN1;
6359           aP0x = aP1x;
6360           aV0x = aV1x;
6361           aDT0x = aDT1x;
6362         }
6363       }
6364
6365       else {
6366         // if current elem is quadratic and current node is not medium
6367         // we have to check - may be it is needed to insert additional nodes
6368         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6369           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6370           if(listNewNodes.size()==aNbTP-1) {
6371             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6372             gp_XYZ P(node->X(), node->Y(), node->Z());
6373             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6374             int i;
6375             for(i=0; i<aNbTP-1; i++) {
6376               const SMDS_MeshNode* N = *it;
6377               double x = ( N->X() + P.X() )/2.;
6378               double y = ( N->Y() + P.Y() )/2.;
6379               double z = ( N->Z() + P.Z() )/2.;
6380               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6381               srcNodes.Append( node );
6382               myLastCreatedNodes.Append(newN);
6383               aNodes[2*i] = newN;
6384               aNodes[2*i+1] = N;
6385               P = gp_XYZ(N->X(),N->Y(),N->Z());
6386             }
6387             listNewNodes.clear();
6388             for(i=0; i<2*(aNbTP-1); i++) {
6389               listNewNodes.push_back(aNodes[i]);
6390             }
6391           }
6392         }
6393       }
6394
6395       newNodesItVec.push_back( nIt );
6396     }
6397     // make new elements
6398     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6399     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6400     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6401   }
6402
6403   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6404
6405   if ( theMakeGroups )
6406     generateGroups( srcNodes, srcElems, "extruded");
6407
6408   return EXTR_OK;
6409 }
6410
6411
6412 //=======================================================================
6413 //function : LinearAngleVariation
6414 //purpose  : auxilary for ExtrusionAlongTrack
6415 //=======================================================================
6416 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6417                                             list<double>& Angles)
6418 {
6419   int nbAngles = Angles.size();
6420   if( nbSteps > nbAngles ) {
6421     vector<double> theAngles(nbAngles);
6422     list<double>::iterator it = Angles.begin();
6423     int i = -1;
6424     for(; it!=Angles.end(); it++) {
6425       i++;
6426       theAngles[i] = (*it);
6427     }
6428     list<double> res;
6429     double rAn2St = double( nbAngles ) / double( nbSteps );
6430     double angPrev = 0, angle;
6431     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6432       double angCur = rAn2St * ( iSt+1 );
6433       double angCurFloor  = floor( angCur );
6434       double angPrevFloor = floor( angPrev );
6435       if ( angPrevFloor == angCurFloor )
6436         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6437       else {
6438         int iP = int( angPrevFloor );
6439         double angPrevCeil = ceil(angPrev);
6440         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6441
6442         int iC = int( angCurFloor );
6443         if ( iC < nbAngles )
6444           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6445
6446         iP = int( angPrevCeil );
6447         while ( iC-- > iP )
6448           angle += theAngles[ iC ];
6449       }
6450       res.push_back(angle);
6451       angPrev = angCur;
6452     }
6453     Angles.clear();
6454     it = res.begin();
6455     for(; it!=res.end(); it++)
6456       Angles.push_back( *it );
6457   }
6458 }
6459
6460
6461 //================================================================================
6462 /*!
6463  * \brief Move or copy theElements applying theTrsf to their nodes
6464  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6465  *  \param theTrsf - transformation to apply
6466  *  \param theCopy - if true, create translated copies of theElems
6467  *  \param theMakeGroups - if true and theCopy, create translated groups
6468  *  \param theTargetMesh - mesh to copy translated elements into
6469  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6470  */
6471 //================================================================================
6472
6473 SMESH_MeshEditor::PGroupIDs
6474 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6475                              const gp_Trsf&     theTrsf,
6476                              const bool         theCopy,
6477                              const bool         theMakeGroups,
6478                              SMESH_Mesh*        theTargetMesh)
6479 {
6480   myLastCreatedElems.Clear();
6481   myLastCreatedNodes.Clear();
6482
6483   bool needReverse = false;
6484   string groupPostfix;
6485   switch ( theTrsf.Form() ) {
6486   case gp_PntMirror:
6487     MESSAGE("gp_PntMirror");
6488     needReverse = true;
6489     groupPostfix = "mirrored";
6490     break;
6491   case gp_Ax1Mirror:
6492     MESSAGE("gp_Ax1Mirror");
6493     groupPostfix = "mirrored";
6494     break;
6495   case gp_Ax2Mirror:
6496     MESSAGE("gp_Ax2Mirror");
6497     needReverse = true;
6498     groupPostfix = "mirrored";
6499     break;
6500   case gp_Rotation:
6501     MESSAGE("gp_Rotation");
6502     groupPostfix = "rotated";
6503     break;
6504   case gp_Translation:
6505     MESSAGE("gp_Translation");
6506     groupPostfix = "translated";
6507     break;
6508   case gp_Scale:
6509     MESSAGE("gp_Scale");
6510     groupPostfix = "scaled";
6511     break;
6512   case gp_CompoundTrsf: // different scale by axis
6513     MESSAGE("gp_CompoundTrsf");
6514     groupPostfix = "scaled";
6515     break;
6516   default:
6517     MESSAGE("default");
6518     needReverse = false;
6519     groupPostfix = "transformed";
6520   }
6521
6522   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6523   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6524   SMESHDS_Mesh* aMesh    = GetMeshDS();
6525
6526
6527   // map old node to new one
6528   TNodeNodeMap nodeMap;
6529
6530   // elements sharing moved nodes; those of them which have all
6531   // nodes mirrored but are not in theElems are to be reversed
6532   TIDSortedElemSet inverseElemSet;
6533
6534   // source elements for each generated one
6535   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6536
6537   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6538   TIDSortedElemSet orphanNode;
6539
6540   if ( theElems.empty() ) // transform the whole mesh
6541   {
6542     // add all elements
6543     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6544     while ( eIt->more() ) theElems.insert( eIt->next() );
6545     // add orphan nodes
6546     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6547     while ( nIt->more() )
6548     {
6549       const SMDS_MeshNode* node = nIt->next();
6550       if ( node->NbInverseElements() == 0)
6551         orphanNode.insert( node );
6552     }
6553   }
6554
6555   // loop on elements to transform nodes : first orphan nodes then elems
6556   TIDSortedElemSet::iterator itElem;
6557   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6558   for (int i=0; i<2; i++)
6559   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6560     const SMDS_MeshElement* elem = *itElem;
6561     if ( !elem )
6562       continue;
6563
6564     // loop on elem nodes
6565     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6566     while ( itN->more() ) {
6567
6568       const SMDS_MeshNode* node = cast2Node( itN->next() );
6569       // check if a node has been already transformed
6570       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6571         nodeMap.insert( make_pair ( node, node ));
6572       if ( !n2n_isnew.second )
6573         continue;
6574
6575       double coord[3];
6576       coord[0] = node->X();
6577       coord[1] = node->Y();
6578       coord[2] = node->Z();
6579       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6580       if ( theTargetMesh ) {
6581         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6582         n2n_isnew.first->second = newNode;
6583         myLastCreatedNodes.Append(newNode);
6584         srcNodes.Append( node );
6585       }
6586       else if ( theCopy ) {
6587         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6588         n2n_isnew.first->second = newNode;
6589         myLastCreatedNodes.Append(newNode);
6590         srcNodes.Append( node );
6591       }
6592       else {
6593         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6594         // node position on shape becomes invalid
6595         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6596           ( SMDS_SpacePosition::originSpacePosition() );
6597       }
6598
6599       // keep inverse elements
6600       if ( !theCopy && !theTargetMesh && needReverse ) {
6601         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6602         while ( invElemIt->more() ) {
6603           const SMDS_MeshElement* iel = invElemIt->next();
6604           inverseElemSet.insert( iel );
6605         }
6606       }
6607     }
6608   }
6609
6610   // either create new elements or reverse mirrored ones
6611   if ( !theCopy && !needReverse && !theTargetMesh )
6612     return PGroupIDs();
6613
6614   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6615   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6616     theElems.insert( *invElemIt );
6617
6618   // Replicate or reverse elements
6619
6620   std::vector<int> iForw;
6621   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6622   {
6623     const SMDS_MeshElement* elem = *itElem;
6624     if ( !elem ) continue;
6625
6626     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6627     int                  nbNodes  = elem->NbNodes();
6628     if ( geomType == SMDSGeom_NONE ) continue; // node
6629
6630     switch ( geomType ) {
6631
6632     case SMDSGeom_POLYGON:  // ---------------------- polygon
6633       {
6634         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6635         int iNode = 0;
6636         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6637         while (itN->more()) {
6638           const SMDS_MeshNode* node =
6639             static_cast<const SMDS_MeshNode*>(itN->next());
6640           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6641           if (nodeMapIt == nodeMap.end())
6642             break; // not all nodes transformed
6643           if (needReverse) {
6644             // reverse mirrored faces and volumes
6645             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6646           } else {
6647             poly_nodes[iNode] = (*nodeMapIt).second;
6648           }
6649           iNode++;
6650         }
6651         if ( iNode != nbNodes )
6652           continue; // not all nodes transformed
6653
6654         if ( theTargetMesh ) {
6655           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6656           srcElems.Append( elem );
6657         }
6658         else if ( theCopy ) {
6659           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6660           srcElems.Append( elem );
6661         }
6662         else {
6663           aMesh->ChangePolygonNodes(elem, poly_nodes);
6664         }
6665       }
6666       break;
6667
6668     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6669       {
6670         const SMDS_VtkVolume* aPolyedre =
6671           dynamic_cast<const SMDS_VtkVolume*>( elem );
6672         if (!aPolyedre) {
6673           MESSAGE("Warning: bad volumic element");
6674           continue;
6675         }
6676
6677         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6678         vector<int> quantities; quantities.reserve( nbNodes );
6679
6680         bool allTransformed = true;
6681         int nbFaces = aPolyedre->NbFaces();
6682         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6683           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6684           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6685             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6686             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6687             if (nodeMapIt == nodeMap.end()) {
6688               allTransformed = false; // not all nodes transformed
6689             } else {
6690               poly_nodes.push_back((*nodeMapIt).second);
6691             }
6692             if ( needReverse && allTransformed )
6693               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6694           }
6695           quantities.push_back(nbFaceNodes);
6696         }
6697         if ( !allTransformed )
6698           continue; // not all nodes transformed
6699
6700         if ( theTargetMesh ) {
6701           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6702           srcElems.Append( elem );
6703         }
6704         else if ( theCopy ) {
6705           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6706           srcElems.Append( elem );
6707         }
6708         else {
6709           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6710         }
6711       }
6712       break;
6713
6714     case SMDSGeom_BALL: // -------------------- Ball
6715       {
6716         if ( !theCopy && !theTargetMesh ) continue;
6717
6718         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6719         if (nodeMapIt == nodeMap.end())
6720           continue; // not all nodes transformed
6721
6722         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6723         if ( theTargetMesh ) {
6724           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6725           srcElems.Append( elem );
6726         }
6727         else {
6728           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6729           srcElems.Append( elem );
6730         }
6731       }
6732       break;
6733
6734     default: // ----------------------- Regular elements
6735
6736       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6737       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6738       const std::vector<int>& i = needReverse ? iRev : iForw;
6739
6740       // find transformed nodes
6741       vector<const SMDS_MeshNode*> nodes(nbNodes);
6742       int iNode = 0;
6743       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6744       while ( itN->more() ) {
6745         const SMDS_MeshNode* node =
6746           static_cast<const SMDS_MeshNode*>( itN->next() );
6747         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6748         if ( nodeMapIt == nodeMap.end() )
6749           break; // not all nodes transformed
6750         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6751       }
6752       if ( iNode != nbNodes )
6753         continue; // not all nodes transformed
6754
6755       if ( theTargetMesh ) {
6756         if ( SMDS_MeshElement* copy =
6757              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6758           myLastCreatedElems.Append( copy );
6759           srcElems.Append( elem );
6760         }
6761       }
6762       else if ( theCopy ) {
6763         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6764           srcElems.Append( elem );
6765       }
6766       else {
6767         // reverse element as it was reversed by transformation
6768         if ( nbNodes > 2 )
6769           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6770       }
6771     } // switch ( geomType )
6772
6773   } // loop on elements
6774
6775   PGroupIDs newGroupIDs;
6776
6777   if ( ( theMakeGroups && theCopy ) ||
6778        ( theMakeGroups && theTargetMesh ) )
6779     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6780
6781   return newGroupIDs;
6782 }
6783
6784 //=======================================================================
6785 /*!
6786  * \brief Create groups of elements made during transformation
6787  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6788  *  \param elemGens - elements making corresponding myLastCreatedElems
6789  *  \param postfix - to append to names of new groups
6790  *  \param targetMesh - mesh to create groups in
6791  *  \param topPresent - is there "top" elements that are created by sweeping
6792  */
6793 //=======================================================================
6794
6795 SMESH_MeshEditor::PGroupIDs
6796 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6797                                  const SMESH_SequenceOfElemPtr& elemGens,
6798                                  const std::string&             postfix,
6799                                  SMESH_Mesh*                    targetMesh,
6800                                  const bool                     topPresent)
6801 {
6802   PGroupIDs newGroupIDs( new list<int> );
6803   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6804
6805   // Sort existing groups by types and collect their names
6806
6807   // containers to store an old group and generated new ones;
6808   // 1st new group is for result elems of different type than a source one;
6809   // 2nd new group is for same type result elems ("top" group at extrusion)
6810   using boost::tuple;
6811   using boost::make_tuple;
6812   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6813   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6814   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6815   // group names
6816   set< string > groupNames;
6817
6818   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6819   if ( !groupIt->more() ) return newGroupIDs;
6820
6821   int newGroupID = mesh->GetGroupIds().back()+1;
6822   while ( groupIt->more() )
6823   {
6824     SMESH_Group * group = groupIt->next();
6825     if ( !group ) continue;
6826     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6827     if ( !groupDS || groupDS->IsEmpty() ) continue;
6828     groupNames.insert    ( group->GetName() );
6829     groupDS->SetStoreName( group->GetName() );
6830     const SMDSAbs_ElementType type = groupDS->GetType();
6831     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6832     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6833     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6834     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6835   }
6836
6837   // Loop on nodes and elements to add them in new groups
6838
6839   vector< const SMDS_MeshElement* > resultElems;
6840   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6841   {
6842     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6843     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6844     if ( gens.Length() != elems.Length() )
6845       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6846
6847     // loop on created elements
6848     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6849     {
6850       const SMDS_MeshElement* sourceElem = gens( iElem );
6851       if ( !sourceElem ) {
6852         MESSAGE("generateGroups(): NULL source element");
6853         continue;
6854       }
6855       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6856       if ( groupsOldNew.empty() ) { // no groups of this type at all
6857         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6858           ++iElem; // skip all elements made by sourceElem
6859         continue;
6860       }
6861       // collect all elements made by the iElem-th sourceElem
6862       resultElems.clear();
6863       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6864         if ( resElem != sourceElem )
6865           resultElems.push_back( resElem );
6866       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6867         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6868           if ( resElem != sourceElem )
6869             resultElems.push_back( resElem );
6870
6871       const SMDS_MeshElement* topElem = 0;
6872       if ( isNodes ) // there must be a top element
6873       {
6874         topElem = resultElems.back();
6875         resultElems.pop_back();
6876       }
6877       else
6878       {
6879         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6880         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6881           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6882           {
6883             topElem = *resElemIt;
6884             *resElemIt = 0; // erase *resElemIt
6885             break;
6886           }
6887       }
6888       // add resultElems to groups originted from ones the sourceElem belongs to
6889       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6890       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6891       {
6892         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6893         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6894         {
6895           // fill in a new group
6896           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6897           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6898           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6899             if ( *resElemIt )
6900               newGroup.Add( *resElemIt );
6901
6902           // fill a "top" group
6903           if ( topElem )
6904           {
6905             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6906             newTopGroup.Add( topElem );
6907          }
6908         }
6909       }
6910     } // loop on created elements
6911   }// loop on nodes and elements
6912
6913   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6914
6915   list<int> topGrouIds;
6916   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6917   {
6918     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6919     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6920                                       orderedOldNewGroups[i]->get<2>() };
6921     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6922     {
6923       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6924       if ( newGroupDS->IsEmpty() )
6925       {
6926         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6927       }
6928       else
6929       {
6930         // set group type
6931         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6932
6933         // make a name
6934         const bool isTop = ( topPresent &&
6935                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6936                              is2nd );
6937
6938         string name = oldGroupDS->GetStoreName();
6939         { // remove trailing whitespaces (issue 22599)
6940           size_t size = name.size();
6941           while ( size > 1 && isspace( name[ size-1 ]))
6942             --size;
6943           if ( size != name.size() )
6944           {
6945             name.resize( size );
6946             oldGroupDS->SetStoreName( name.c_str() );
6947           }
6948         }
6949         if ( !targetMesh ) {
6950           string suffix = ( isTop ? "top": postfix.c_str() );
6951           name += "_";
6952           name += suffix;
6953           int nb = 1;
6954           while ( !groupNames.insert( name ).second ) // name exists
6955             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6956         }
6957         else if ( isTop ) {
6958           name += "_top";
6959         }
6960         newGroupDS->SetStoreName( name.c_str() );
6961
6962         // make a SMESH_Groups
6963         mesh->AddGroup( newGroupDS );
6964         if ( isTop )
6965           topGrouIds.push_back( newGroupDS->GetID() );
6966         else
6967           newGroupIDs->push_back( newGroupDS->GetID() );
6968       }
6969     }
6970   }
6971   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6972
6973   return newGroupIDs;
6974 }
6975
6976 //================================================================================
6977 /*!
6978  * \brief Return list of group of nodes close to each other within theTolerance
6979  *        Search among theNodes or in the whole mesh if theNodes is empty using
6980  *        an Octree algorithm
6981  */
6982 //================================================================================
6983
6984 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6985                                             const double         theTolerance,
6986                                             TListOfListOfNodes & theGroupsOfNodes)
6987 {
6988   myLastCreatedElems.Clear();
6989   myLastCreatedNodes.Clear();
6990
6991   if ( theNodes.empty() )
6992   { // get all nodes in the mesh
6993     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6994     while ( nIt->more() )
6995       theNodes.insert( theNodes.end(),nIt->next());
6996   }
6997
6998   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6999 }
7000
7001 //=======================================================================
7002 //function : SimplifyFace
7003 //purpose  :
7004 //=======================================================================
7005
7006 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7007                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7008                                     vector<int>&                         quantities) const
7009 {
7010   int nbNodes = faceNodes.size();
7011
7012   if (nbNodes < 3)
7013     return 0;
7014
7015   set<const SMDS_MeshNode*> nodeSet;
7016
7017   // get simple seq of nodes
7018   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7019   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7020   int iSimple = 0, nbUnique = 0;
7021
7022   simpleNodes[iSimple++] = faceNodes[0];
7023   nbUnique++;
7024   for (int iCur = 1; iCur < nbNodes; iCur++) {
7025     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7026       simpleNodes[iSimple++] = faceNodes[iCur];
7027       if (nodeSet.insert( faceNodes[iCur] ).second)
7028         nbUnique++;
7029     }
7030   }
7031   int nbSimple = iSimple;
7032   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7033     nbSimple--;
7034     iSimple--;
7035   }
7036
7037   if (nbUnique < 3)
7038     return 0;
7039
7040   // separate loops
7041   int nbNew = 0;
7042   bool foundLoop = (nbSimple > nbUnique);
7043   while (foundLoop) {
7044     foundLoop = false;
7045     set<const SMDS_MeshNode*> loopSet;
7046     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7047       const SMDS_MeshNode* n = simpleNodes[iSimple];
7048       if (!loopSet.insert( n ).second) {
7049         foundLoop = true;
7050
7051         // separate loop
7052         int iC = 0, curLast = iSimple;
7053         for (; iC < curLast; iC++) {
7054           if (simpleNodes[iC] == n) break;
7055         }
7056         int loopLen = curLast - iC;
7057         if (loopLen > 2) {
7058           // create sub-element
7059           nbNew++;
7060           quantities.push_back(loopLen);
7061           for (; iC < curLast; iC++) {
7062             poly_nodes.push_back(simpleNodes[iC]);
7063           }
7064         }
7065         // shift the rest nodes (place from the first loop position)
7066         for (iC = curLast + 1; iC < nbSimple; iC++) {
7067           simpleNodes[iC - loopLen] = simpleNodes[iC];
7068         }
7069         nbSimple -= loopLen;
7070         iSimple -= loopLen;
7071       }
7072     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7073   } // while (foundLoop)
7074
7075   if (iSimple > 2) {
7076     nbNew++;
7077     quantities.push_back(iSimple);
7078     for (int i = 0; i < iSimple; i++)
7079       poly_nodes.push_back(simpleNodes[i]);
7080   }
7081
7082   return nbNew;
7083 }
7084
7085 //=======================================================================
7086 //function : MergeNodes
7087 //purpose  : In each group, the cdr of nodes are substituted by the first one
7088 //           in all elements.
7089 //=======================================================================
7090
7091 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7092 {
7093   MESSAGE("MergeNodes");
7094   myLastCreatedElems.Clear();
7095   myLastCreatedNodes.Clear();
7096
7097   SMESHDS_Mesh* aMesh = GetMeshDS();
7098
7099   TNodeNodeMap nodeNodeMap; // node to replace - new node
7100   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7101   list< int > rmElemIds, rmNodeIds;
7102
7103   // Fill nodeNodeMap and elems
7104
7105   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7106   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7107     list<const SMDS_MeshNode*>& nodes = *grIt;
7108     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7109     const SMDS_MeshNode* nToKeep = *nIt;
7110     //MESSAGE("node to keep " << nToKeep->GetID());
7111     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7112       const SMDS_MeshNode* nToRemove = *nIt;
7113       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7114       if ( nToRemove != nToKeep ) {
7115         //MESSAGE("  node to remove " << nToRemove->GetID());
7116         rmNodeIds.push_back( nToRemove->GetID() );
7117         AddToSameGroups( nToKeep, nToRemove, aMesh );
7118         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7119         // after MergeNodes() w/o creating node in place of merged ones.
7120         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7121         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7122           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7123             sm->SetIsAlwaysComputed( true );
7124       }
7125
7126       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7127       while ( invElemIt->more() ) {
7128         const SMDS_MeshElement* elem = invElemIt->next();
7129         elems.insert(elem);
7130       }
7131     }
7132   }
7133   // Change element nodes or remove an element
7134
7135   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7136   for ( ; eIt != elems.end(); eIt++ ) {
7137     const SMDS_MeshElement* elem = *eIt;
7138     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7139     int nbNodes = elem->NbNodes();
7140     int aShapeId = FindShape( elem );
7141
7142     set<const SMDS_MeshNode*> nodeSet;
7143     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7144     int iUnique = 0, iCur = 0, nbRepl = 0;
7145     vector<int> iRepl( nbNodes );
7146
7147     // get new seq of nodes
7148     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7149     while ( itN->more() ) {
7150       const SMDS_MeshNode* n =
7151         static_cast<const SMDS_MeshNode*>( itN->next() );
7152
7153       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7154       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7155         n = (*nnIt).second;
7156         // BUG 0020185: begin
7157         {
7158           bool stopRecur = false;
7159           set<const SMDS_MeshNode*> nodesRecur;
7160           nodesRecur.insert(n);
7161           while (!stopRecur) {
7162             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7163             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7164               n = (*nnIt_i).second;
7165               if (!nodesRecur.insert(n).second) {
7166                 // error: recursive dependancy
7167                 stopRecur = true;
7168               }
7169             }
7170             else
7171               stopRecur = true;
7172           }
7173         }
7174         // BUG 0020185: end
7175       }
7176       curNodes[ iCur ] = n;
7177       bool isUnique = nodeSet.insert( n ).second;
7178       if ( isUnique )
7179         uniqueNodes[ iUnique++ ] = n;
7180       else
7181         iRepl[ nbRepl++ ] = iCur;
7182       iCur++;
7183     }
7184
7185     // Analyse element topology after replacement
7186
7187     bool isOk = true;
7188     int nbUniqueNodes = nodeSet.size();
7189     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7190     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7191       // Polygons and Polyhedral volumes
7192       if (elem->IsPoly()) {
7193
7194         if (elem->GetType() == SMDSAbs_Face) {
7195           // Polygon
7196           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7197           int inode = 0;
7198           for (; inode < nbNodes; inode++) {
7199             face_nodes[inode] = curNodes[inode];
7200           }
7201
7202           vector<const SMDS_MeshNode *> polygons_nodes;
7203           vector<int> quantities;
7204           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7205           if (nbNew > 0) {
7206             inode = 0;
7207             for (int iface = 0; iface < nbNew; iface++) {
7208               int nbNodes = quantities[iface];
7209               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7210               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7211                 poly_nodes[ii] = polygons_nodes[inode];
7212               }
7213               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7214               myLastCreatedElems.Append(newElem);
7215               if (aShapeId)
7216                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7217             }
7218
7219             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7220             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7221             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7222             int quid =0;
7223             if (nbNew > 0) quid = nbNew - 1;
7224             vector<int> newquant(quantities.begin()+quid, quantities.end());
7225             const SMDS_MeshElement* newElem = 0;
7226             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7227             myLastCreatedElems.Append(newElem);
7228             if ( aShapeId && newElem )
7229               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7230             rmElemIds.push_back(elem->GetID());
7231           }
7232           else {
7233             rmElemIds.push_back(elem->GetID());
7234           }
7235
7236         }
7237         else if (elem->GetType() == SMDSAbs_Volume) {
7238           // Polyhedral volume
7239           if (nbUniqueNodes < 4) {
7240             rmElemIds.push_back(elem->GetID());
7241           }
7242           else {
7243             // each face has to be analyzed in order to check volume validity
7244             const SMDS_VtkVolume* aPolyedre =
7245               dynamic_cast<const SMDS_VtkVolume*>( elem );
7246             if (aPolyedre) {
7247               int nbFaces = aPolyedre->NbFaces();
7248
7249               vector<const SMDS_MeshNode *> poly_nodes;
7250               vector<int> quantities;
7251
7252               for (int iface = 1; iface <= nbFaces; iface++) {
7253                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7254                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7255
7256                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7257                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7258                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7259                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7260                     faceNode = (*nnIt).second;
7261                   }
7262                   faceNodes[inode - 1] = faceNode;
7263                 }
7264
7265                 SimplifyFace(faceNodes, poly_nodes, quantities);
7266               }
7267
7268               if (quantities.size() > 3) {
7269                 // to be done: remove coincident faces
7270               }
7271
7272               if (quantities.size() > 3)
7273                 {
7274                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7275                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7276                   const SMDS_MeshElement* newElem = 0;
7277                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7278                   myLastCreatedElems.Append(newElem);
7279                   if ( aShapeId && newElem )
7280                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7281                   rmElemIds.push_back(elem->GetID());
7282                 }
7283             }
7284             else {
7285               rmElemIds.push_back(elem->GetID());
7286             }
7287           }
7288         }
7289         else {
7290         }
7291
7292         continue;
7293       } // poly element
7294
7295       // Regular elements
7296       // TODO not all the possible cases are solved. Find something more generic?
7297       switch ( nbNodes ) {
7298       case 2: ///////////////////////////////////// EDGE
7299         isOk = false; break;
7300       case 3: ///////////////////////////////////// TRIANGLE
7301         isOk = false; break;
7302       case 4:
7303         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7304           isOk = false;
7305         else { //////////////////////////////////// QUADRANGLE
7306           if ( nbUniqueNodes < 3 )
7307             isOk = false;
7308           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7309             isOk = false; // opposite nodes stick
7310           //MESSAGE("isOk " << isOk);
7311         }
7312         break;
7313       case 6: ///////////////////////////////////// PENTAHEDRON
7314         if ( nbUniqueNodes == 4 ) {
7315           // ---------------------------------> tetrahedron
7316           if (nbRepl == 3 &&
7317               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7318             // all top nodes stick: reverse a bottom
7319             uniqueNodes[ 0 ] = curNodes [ 1 ];
7320             uniqueNodes[ 1 ] = curNodes [ 0 ];
7321           }
7322           else if (nbRepl == 3 &&
7323                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7324             // all bottom nodes stick: set a top before
7325             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7326             uniqueNodes[ 0 ] = curNodes [ 3 ];
7327             uniqueNodes[ 1 ] = curNodes [ 4 ];
7328             uniqueNodes[ 2 ] = curNodes [ 5 ];
7329           }
7330           else if (nbRepl == 4 &&
7331                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7332             // a lateral face turns into a line: reverse a bottom
7333             uniqueNodes[ 0 ] = curNodes [ 1 ];
7334             uniqueNodes[ 1 ] = curNodes [ 0 ];
7335           }
7336           else
7337             isOk = false;
7338         }
7339         else if ( nbUniqueNodes == 5 ) {
7340           // PENTAHEDRON --------------------> 2 tetrahedrons
7341           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7342             // a bottom node sticks with a linked top one
7343             // 1.
7344             SMDS_MeshElement* newElem =
7345               aMesh->AddVolume(curNodes[ 3 ],
7346                                curNodes[ 4 ],
7347                                curNodes[ 5 ],
7348                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7349             myLastCreatedElems.Append(newElem);
7350             if ( aShapeId )
7351               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7352             // 2. : reverse a bottom
7353             uniqueNodes[ 0 ] = curNodes [ 1 ];
7354             uniqueNodes[ 1 ] = curNodes [ 0 ];
7355             nbUniqueNodes = 4;
7356           }
7357           else
7358             isOk = false;
7359         }
7360         else
7361           isOk = false;
7362         break;
7363       case 8: {
7364         if(elem->IsQuadratic()) { // Quadratic quadrangle
7365           //   1    5    2
7366           //    +---+---+
7367           //    |       |
7368           //    |       |
7369           //   4+       +6
7370           //    |       |
7371           //    |       |
7372           //    +---+---+
7373           //   0    7    3
7374           isOk = false;
7375           if(nbRepl==2) {
7376             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7377           }
7378           if(nbRepl==3) {
7379             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7380             nbUniqueNodes = 6;
7381             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7382               uniqueNodes[0] = curNodes[0];
7383               uniqueNodes[1] = curNodes[2];
7384               uniqueNodes[2] = curNodes[3];
7385               uniqueNodes[3] = curNodes[5];
7386               uniqueNodes[4] = curNodes[6];
7387               uniqueNodes[5] = curNodes[7];
7388               isOk = true;
7389             }
7390             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7391               uniqueNodes[0] = curNodes[0];
7392               uniqueNodes[1] = curNodes[1];
7393               uniqueNodes[2] = curNodes[2];
7394               uniqueNodes[3] = curNodes[4];
7395               uniqueNodes[4] = curNodes[5];
7396               uniqueNodes[5] = curNodes[6];
7397               isOk = true;
7398             }
7399             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7400               uniqueNodes[0] = curNodes[1];
7401               uniqueNodes[1] = curNodes[2];
7402               uniqueNodes[2] = curNodes[3];
7403               uniqueNodes[3] = curNodes[5];
7404               uniqueNodes[4] = curNodes[6];
7405               uniqueNodes[5] = curNodes[0];
7406               isOk = true;
7407             }
7408             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7409               uniqueNodes[0] = curNodes[0];
7410               uniqueNodes[1] = curNodes[1];
7411               uniqueNodes[2] = curNodes[3];
7412               uniqueNodes[3] = curNodes[4];
7413               uniqueNodes[4] = curNodes[6];
7414               uniqueNodes[5] = curNodes[7];
7415               isOk = true;
7416             }
7417             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7418               uniqueNodes[0] = curNodes[0];
7419               uniqueNodes[1] = curNodes[2];
7420               uniqueNodes[2] = curNodes[3];
7421               uniqueNodes[3] = curNodes[1];
7422               uniqueNodes[4] = curNodes[6];
7423               uniqueNodes[5] = curNodes[7];
7424               isOk = true;
7425             }
7426             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7427               uniqueNodes[0] = curNodes[0];
7428               uniqueNodes[1] = curNodes[1];
7429               uniqueNodes[2] = curNodes[2];
7430               uniqueNodes[3] = curNodes[4];
7431               uniqueNodes[4] = curNodes[5];
7432               uniqueNodes[5] = curNodes[7];
7433               isOk = true;
7434             }
7435             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7436               uniqueNodes[0] = curNodes[0];
7437               uniqueNodes[1] = curNodes[1];
7438               uniqueNodes[2] = curNodes[3];
7439               uniqueNodes[3] = curNodes[4];
7440               uniqueNodes[4] = curNodes[2];
7441               uniqueNodes[5] = curNodes[7];
7442               isOk = true;
7443             }
7444             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7445               uniqueNodes[0] = curNodes[0];
7446               uniqueNodes[1] = curNodes[1];
7447               uniqueNodes[2] = curNodes[2];
7448               uniqueNodes[3] = curNodes[4];
7449               uniqueNodes[4] = curNodes[5];
7450               uniqueNodes[5] = curNodes[3];
7451               isOk = true;
7452             }
7453           }
7454           if(nbRepl==4) {
7455             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7456           }
7457           if(nbRepl==5) {
7458             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7459           }
7460           break;
7461         }
7462         //////////////////////////////////// HEXAHEDRON
7463         isOk = false;
7464         SMDS_VolumeTool hexa (elem);
7465         hexa.SetExternalNormal();
7466         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7467           //////////////////////// HEX ---> 1 tetrahedron
7468           for ( int iFace = 0; iFace < 6; iFace++ ) {
7469             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7470             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7471                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7472                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7473               // one face turns into a point ...
7474               int iOppFace = hexa.GetOppFaceIndex( iFace );
7475               ind = hexa.GetFaceNodesIndices( iOppFace );
7476               int nbStick = 0;
7477               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7478                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7479                   nbStick++;
7480               }
7481               if ( nbStick == 1 ) {
7482                 // ... and the opposite one - into a triangle.
7483                 // set a top node
7484                 ind = hexa.GetFaceNodesIndices( iFace );
7485                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7486                 isOk = true;
7487               }
7488               break;
7489             }
7490           }
7491         }
7492         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7493           //////////////////////// HEX ---> 1 prism
7494           int nbTria = 0, iTria[3];
7495           const int *ind; // indices of face nodes
7496           // look for triangular faces
7497           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7498             ind = hexa.GetFaceNodesIndices( iFace );
7499             TIDSortedNodeSet faceNodes;
7500             for ( iCur = 0; iCur < 4; iCur++ )
7501               faceNodes.insert( curNodes[ind[iCur]] );
7502             if ( faceNodes.size() == 3 )
7503               iTria[ nbTria++ ] = iFace;
7504           }
7505           // check if triangles are opposite
7506           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7507           {
7508             isOk = true;
7509             // set nodes of the bottom triangle
7510             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7511             vector<int> indB;
7512             for ( iCur = 0; iCur < 4; iCur++ )
7513               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7514                 indB.push_back( ind[iCur] );
7515             if ( !hexa.IsForward() )
7516               std::swap( indB[0], indB[2] );
7517             for ( iCur = 0; iCur < 3; iCur++ )
7518               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7519             // set nodes of the top triangle
7520             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7521             for ( iCur = 0; iCur < 3; ++iCur )
7522               for ( int j = 0; j < 4; ++j )
7523                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7524                 {
7525                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7526                   break;
7527                 }
7528           }
7529           break;
7530         }
7531         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7532           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7533           for ( int iFace = 0; iFace < 6; iFace++ ) {
7534             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7535             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7536                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7537                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7538               // one face turns into a point ...
7539               int iOppFace = hexa.GetOppFaceIndex( iFace );
7540               ind = hexa.GetFaceNodesIndices( iOppFace );
7541               int nbStick = 0;
7542               iUnique = 2;  // reverse a tetrahedron 1 bottom
7543               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7544                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7545                   nbStick++;
7546                 else if ( iUnique >= 0 )
7547                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7548               }
7549               if ( nbStick == 0 ) {
7550                 // ... and the opposite one is a quadrangle
7551                 // set a top node
7552                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7553                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7554                 nbUniqueNodes = 4;
7555                 // tetrahedron 2
7556                 SMDS_MeshElement* newElem =
7557                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7558                                    curNodes[ind[ 3 ]],
7559                                    curNodes[ind[ 2 ]],
7560                                    curNodes[indTop[ 0 ]]);
7561                 myLastCreatedElems.Append(newElem);
7562                 if ( aShapeId )
7563                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7564                 isOk = true;
7565               }
7566               break;
7567             }
7568           }
7569         }
7570         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7571           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7572           // find indices of quad and tri faces
7573           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7574           for ( iFace = 0; iFace < 6; iFace++ ) {
7575             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7576             nodeSet.clear();
7577             for ( iCur = 0; iCur < 4; iCur++ )
7578               nodeSet.insert( curNodes[ind[ iCur ]] );
7579             nbUniqueNodes = nodeSet.size();
7580             if ( nbUniqueNodes == 3 )
7581               iTriFace[ nbTri++ ] = iFace;
7582             else if ( nbUniqueNodes == 4 )
7583               iQuadFace[ nbQuad++ ] = iFace;
7584           }
7585           if (nbQuad == 2 && nbTri == 4 &&
7586               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7587             // 2 opposite quadrangles stuck with a diagonal;
7588             // sample groups of merged indices: (0-4)(2-6)
7589             // --------------------------------------------> 2 tetrahedrons
7590             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7591             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7592             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7593             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7594                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7595               // stuck with 0-2 diagonal
7596               i0  = ind1[ 3 ];
7597               i1d = ind1[ 0 ];
7598               i2  = ind1[ 1 ];
7599               i3d = ind1[ 2 ];
7600               i0t = ind2[ 1 ];
7601               i2t = ind2[ 3 ];
7602             }
7603             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7604                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7605               // stuck with 1-3 diagonal
7606               i0  = ind1[ 0 ];
7607               i1d = ind1[ 1 ];
7608               i2  = ind1[ 2 ];
7609               i3d = ind1[ 3 ];
7610               i0t = ind2[ 0 ];
7611               i2t = ind2[ 1 ];
7612             }
7613             else {
7614               ASSERT(0);
7615             }
7616             // tetrahedron 1
7617             uniqueNodes[ 0 ] = curNodes [ i0 ];
7618             uniqueNodes[ 1 ] = curNodes [ i1d ];
7619             uniqueNodes[ 2 ] = curNodes [ i3d ];
7620             uniqueNodes[ 3 ] = curNodes [ i0t ];
7621             nbUniqueNodes = 4;
7622             // tetrahedron 2
7623             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7624                                                          curNodes[ i2 ],
7625                                                          curNodes[ i3d ],
7626                                                          curNodes[ i2t ]);
7627             myLastCreatedElems.Append(newElem);
7628             if ( aShapeId )
7629               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7630             isOk = true;
7631           }
7632           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7633                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7634             // --------------------------------------------> prism
7635             // find 2 opposite triangles
7636             nbUniqueNodes = 6;
7637             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7638               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7639                 // find indices of kept and replaced nodes
7640                 // and fill unique nodes of 2 opposite triangles
7641                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7642                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7643                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7644                 // fill unique nodes
7645                 iUnique = 0;
7646                 isOk = true;
7647                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7648                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7649                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7650                   if ( n == nInit ) {
7651                     // iCur of a linked node of the opposite face (make normals co-directed):
7652                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7653                     // check that correspondent corners of triangles are linked
7654                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7655                       isOk = false;
7656                     else {
7657                       uniqueNodes[ iUnique ] = n;
7658                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7659                       iUnique++;
7660                     }
7661                   }
7662                 }
7663                 break;
7664               }
7665             }
7666           }
7667         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7668         else
7669         {
7670           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7671         }
7672         break;
7673       } // HEXAHEDRON
7674
7675       default:
7676         isOk = false;
7677       } // switch ( nbNodes )
7678
7679     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7680
7681     if ( isOk ) { // the elem remains valid after sticking nodes
7682       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7683       {
7684         // Change nodes of polyedre
7685         const SMDS_VtkVolume* aPolyedre =
7686           dynamic_cast<const SMDS_VtkVolume*>( elem );
7687         if (aPolyedre) {
7688           int nbFaces = aPolyedre->NbFaces();
7689
7690           vector<const SMDS_MeshNode *> poly_nodes;
7691           vector<int> quantities (nbFaces);
7692
7693           for (int iface = 1; iface <= nbFaces; iface++) {
7694             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7695             quantities[iface - 1] = nbFaceNodes;
7696
7697             for (inode = 1; inode <= nbFaceNodes; inode++) {
7698               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7699
7700               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7701               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7702                 curNode = (*nnIt).second;
7703               }
7704               poly_nodes.push_back(curNode);
7705             }
7706           }
7707           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7708         }
7709       }
7710       else // replace non-polyhedron elements
7711       {
7712         const SMDSAbs_ElementType etyp = elem->GetType();
7713         const int elemId               = elem->GetID();
7714         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7715         uniqueNodes.resize(nbUniqueNodes);
7716
7717         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7718
7719         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7720         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7721         if ( sm && newElem )
7722           sm->AddElement( newElem );
7723         if ( elem != newElem )
7724           ReplaceElemInGroups( elem, newElem, aMesh );
7725       }
7726     }
7727     else {
7728       // Remove invalid regular element or invalid polygon
7729       rmElemIds.push_back( elem->GetID() );
7730     }
7731
7732   } // loop on elements
7733
7734   // Remove bad elements, then equal nodes (order important)
7735
7736   Remove( rmElemIds, false );
7737   Remove( rmNodeIds, true );
7738
7739 }
7740
7741
7742 // ========================================================
7743 // class   : SortableElement
7744 // purpose : allow sorting elements basing on their nodes
7745 // ========================================================
7746 class SortableElement : public set <const SMDS_MeshElement*>
7747 {
7748 public:
7749
7750   SortableElement( const SMDS_MeshElement* theElem )
7751   {
7752     myElem = theElem;
7753     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7754     while ( nodeIt->more() )
7755       this->insert( nodeIt->next() );
7756   }
7757
7758   const SMDS_MeshElement* Get() const
7759   { return myElem; }
7760
7761   void Set(const SMDS_MeshElement* e) const
7762   { myElem = e; }
7763
7764
7765 private:
7766   mutable const SMDS_MeshElement* myElem;
7767 };
7768
7769 //=======================================================================
7770 //function : FindEqualElements
7771 //purpose  : Return list of group of elements built on the same nodes.
7772 //           Search among theElements or in the whole mesh if theElements is empty
7773 //=======================================================================
7774
7775 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7776                                          TListOfListOfElementsID & theGroupsOfElementsID)
7777 {
7778   myLastCreatedElems.Clear();
7779   myLastCreatedNodes.Clear();
7780
7781   typedef map< SortableElement, int > TMapOfNodeSet;
7782   typedef list<int> TGroupOfElems;
7783
7784   if ( theElements.empty() )
7785   { // get all elements in the mesh
7786     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7787     while ( eIt->more() )
7788       theElements.insert( theElements.end(), eIt->next());
7789   }
7790
7791   vector< TGroupOfElems > arrayOfGroups;
7792   TGroupOfElems groupOfElems;
7793   TMapOfNodeSet mapOfNodeSet;
7794
7795   TIDSortedElemSet::iterator elemIt = theElements.begin();
7796   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7797     const SMDS_MeshElement* curElem = *elemIt;
7798     SortableElement SE(curElem);
7799     int ind = -1;
7800     // check uniqueness
7801     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7802     if( !(pp.second) ) {
7803       TMapOfNodeSet::iterator& itSE = pp.first;
7804       ind = (*itSE).second;
7805       arrayOfGroups[ind].push_back(curElem->GetID());
7806     }
7807     else {
7808       groupOfElems.clear();
7809       groupOfElems.push_back(curElem->GetID());
7810       arrayOfGroups.push_back(groupOfElems);
7811       i++;
7812     }
7813   }
7814
7815   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7816   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7817     groupOfElems = *groupIt;
7818     if ( groupOfElems.size() > 1 ) {
7819       groupOfElems.sort();
7820       theGroupsOfElementsID.push_back(groupOfElems);
7821     }
7822   }
7823 }
7824
7825 //=======================================================================
7826 //function : MergeElements
7827 //purpose  : In each given group, substitute all elements by the first one.
7828 //=======================================================================
7829
7830 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7831 {
7832   myLastCreatedElems.Clear();
7833   myLastCreatedNodes.Clear();
7834
7835   typedef list<int> TListOfIDs;
7836   TListOfIDs rmElemIds; // IDs of elems to remove
7837
7838   SMESHDS_Mesh* aMesh = GetMeshDS();
7839
7840   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7841   while ( groupsIt != theGroupsOfElementsID.end() ) {
7842     TListOfIDs& aGroupOfElemID = *groupsIt;
7843     aGroupOfElemID.sort();
7844     int elemIDToKeep = aGroupOfElemID.front();
7845     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7846     aGroupOfElemID.pop_front();
7847     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7848     while ( idIt != aGroupOfElemID.end() ) {
7849       int elemIDToRemove = *idIt;
7850       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7851       // add the kept element in groups of removed one (PAL15188)
7852       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7853       rmElemIds.push_back( elemIDToRemove );
7854       ++idIt;
7855     }
7856     ++groupsIt;
7857   }
7858
7859   Remove( rmElemIds, false );
7860 }
7861
7862 //=======================================================================
7863 //function : MergeEqualElements
7864 //purpose  : Remove all but one of elements built on the same nodes.
7865 //=======================================================================
7866
7867 void SMESH_MeshEditor::MergeEqualElements()
7868 {
7869   TIDSortedElemSet aMeshElements; /* empty input ==
7870                                      to merge equal elements in the whole mesh */
7871   TListOfListOfElementsID aGroupsOfElementsID;
7872   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7873   MergeElements(aGroupsOfElementsID);
7874 }
7875
7876 //=======================================================================
7877 //function : findAdjacentFace
7878 //purpose  :
7879 //=======================================================================
7880
7881 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7882                                                 const SMDS_MeshNode* n2,
7883                                                 const SMDS_MeshElement* elem)
7884 {
7885   TIDSortedElemSet elemSet, avoidSet;
7886   if ( elem )
7887     avoidSet.insert ( elem );
7888   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7889 }
7890
7891 //=======================================================================
7892 //function : FindFreeBorder
7893 //purpose  :
7894 //=======================================================================
7895
7896 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7897
7898 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7899                                        const SMDS_MeshNode*             theSecondNode,
7900                                        const SMDS_MeshNode*             theLastNode,
7901                                        list< const SMDS_MeshNode* > &   theNodes,
7902                                        list< const SMDS_MeshElement* >& theFaces)
7903 {
7904   if ( !theFirstNode || !theSecondNode )
7905     return false;
7906   // find border face between theFirstNode and theSecondNode
7907   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7908   if ( !curElem )
7909     return false;
7910
7911   theFaces.push_back( curElem );
7912   theNodes.push_back( theFirstNode );
7913   theNodes.push_back( theSecondNode );
7914
7915   //vector<const SMDS_MeshNode*> nodes;
7916   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7917   TIDSortedElemSet foundElems;
7918   bool needTheLast = ( theLastNode != 0 );
7919
7920   while ( nStart != theLastNode ) {
7921     if ( nStart == theFirstNode )
7922       return !needTheLast;
7923
7924     // find all free border faces sharing form nStart
7925
7926     list< const SMDS_MeshElement* > curElemList;
7927     list< const SMDS_MeshNode* > nStartList;
7928     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7929     while ( invElemIt->more() ) {
7930       const SMDS_MeshElement* e = invElemIt->next();
7931       if ( e == curElem || foundElems.insert( e ).second ) {
7932         // get nodes
7933         int iNode = 0, nbNodes = e->NbNodes();
7934         //const SMDS_MeshNode* nodes[nbNodes+1];
7935         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7936
7937         if(e->IsQuadratic()) {
7938           const SMDS_VtkFace* F =
7939             dynamic_cast<const SMDS_VtkFace*>(e);
7940           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7941           // use special nodes iterator
7942           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7943           while( anIter->more() ) {
7944             nodes[ iNode++ ] = cast2Node(anIter->next());
7945           }
7946         }
7947         else {
7948           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7949           while ( nIt->more() )
7950             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7951         }
7952         nodes[ iNode ] = nodes[ 0 ];
7953         // check 2 links
7954         for ( iNode = 0; iNode < nbNodes; iNode++ )
7955           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7956                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7957               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7958           {
7959             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7960             curElemList.push_back( e );
7961           }
7962       }
7963     }
7964     // analyse the found
7965
7966     int nbNewBorders = curElemList.size();
7967     if ( nbNewBorders == 0 ) {
7968       // no free border furthermore
7969       return !needTheLast;
7970     }
7971     else if ( nbNewBorders == 1 ) {
7972       // one more element found
7973       nIgnore = nStart;
7974       nStart = nStartList.front();
7975       curElem = curElemList.front();
7976       theFaces.push_back( curElem );
7977       theNodes.push_back( nStart );
7978     }
7979     else {
7980       // several continuations found
7981       list< const SMDS_MeshElement* >::iterator curElemIt;
7982       list< const SMDS_MeshNode* >::iterator nStartIt;
7983       // check if one of them reached the last node
7984       if ( needTheLast ) {
7985         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7986              curElemIt!= curElemList.end();
7987              curElemIt++, nStartIt++ )
7988           if ( *nStartIt == theLastNode ) {
7989             theFaces.push_back( *curElemIt );
7990             theNodes.push_back( *nStartIt );
7991             return true;
7992           }
7993       }
7994       // find the best free border by the continuations
7995       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7996       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7997       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7998            curElemIt!= curElemList.end();
7999            curElemIt++, nStartIt++ )
8000       {
8001         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8002         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8003         // find one more free border
8004         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8005           cNL->clear();
8006           cFL->clear();
8007         }
8008         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8009           // choice: clear a worse one
8010           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8011           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8012           contNodes[ iWorse ].clear();
8013           contFaces[ iWorse ].clear();
8014         }
8015       }
8016       if ( contNodes[0].empty() && contNodes[1].empty() )
8017         return false;
8018
8019       // append the best free border
8020       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8021       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8022       theNodes.pop_back(); // remove nIgnore
8023       theNodes.pop_back(); // remove nStart
8024       theFaces.pop_back(); // remove curElem
8025       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8026       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8027       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8028       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8029       return true;
8030
8031     } // several continuations found
8032   } // while ( nStart != theLastNode )
8033
8034   return true;
8035 }
8036
8037 //=======================================================================
8038 //function : CheckFreeBorderNodes
8039 //purpose  : Return true if the tree nodes are on a free border
8040 //=======================================================================
8041
8042 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8043                                             const SMDS_MeshNode* theNode2,
8044                                             const SMDS_MeshNode* theNode3)
8045 {
8046   list< const SMDS_MeshNode* > nodes;
8047   list< const SMDS_MeshElement* > faces;
8048   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8049 }
8050
8051 //=======================================================================
8052 //function : SewFreeBorder
8053 //purpose  :
8054 //=======================================================================
8055
8056 SMESH_MeshEditor::Sew_Error
8057 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8058                                  const SMDS_MeshNode* theBordSecondNode,
8059                                  const SMDS_MeshNode* theBordLastNode,
8060                                  const SMDS_MeshNode* theSideFirstNode,
8061                                  const SMDS_MeshNode* theSideSecondNode,
8062                                  const SMDS_MeshNode* theSideThirdNode,
8063                                  const bool           theSideIsFreeBorder,
8064                                  const bool           toCreatePolygons,
8065                                  const bool           toCreatePolyedrs)
8066 {
8067   myLastCreatedElems.Clear();
8068   myLastCreatedNodes.Clear();
8069
8070   MESSAGE("::SewFreeBorder()");
8071   Sew_Error aResult = SEW_OK;
8072
8073   // ====================================
8074   //    find side nodes and elements
8075   // ====================================
8076
8077   list< const SMDS_MeshNode* > nSide[ 2 ];
8078   list< const SMDS_MeshElement* > eSide[ 2 ];
8079   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8080   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8081
8082   // Free border 1
8083   // --------------
8084   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8085                       nSide[0], eSide[0])) {
8086     MESSAGE(" Free Border 1 not found " );
8087     aResult = SEW_BORDER1_NOT_FOUND;
8088   }
8089   if (theSideIsFreeBorder) {
8090     // Free border 2
8091     // --------------
8092     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8093                         nSide[1], eSide[1])) {
8094       MESSAGE(" Free Border 2 not found " );
8095       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8096     }
8097   }
8098   if ( aResult != SEW_OK )
8099     return aResult;
8100
8101   if (!theSideIsFreeBorder) {
8102     // Side 2
8103     // --------------
8104
8105     // -------------------------------------------------------------------------
8106     // Algo:
8107     // 1. If nodes to merge are not coincident, move nodes of the free border
8108     //    from the coord sys defined by the direction from the first to last
8109     //    nodes of the border to the correspondent sys of the side 2
8110     // 2. On the side 2, find the links most co-directed with the correspondent
8111     //    links of the free border
8112     // -------------------------------------------------------------------------
8113
8114     // 1. Since sewing may break if there are volumes to split on the side 2,
8115     //    we wont move nodes but just compute new coordinates for them
8116     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8117     TNodeXYZMap nBordXYZ;
8118     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8119     list< const SMDS_MeshNode* >::iterator nBordIt;
8120
8121     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8122     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8123     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8124     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8125     double tol2 = 1.e-8;
8126     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8127     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8128       // Need node movement.
8129
8130       // find X and Z axes to create trsf
8131       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8132       gp_Vec X = Zs ^ Zb;
8133       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8134         // Zb || Zs
8135         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8136
8137       // coord systems
8138       gp_Ax3 toBordAx( Pb1, Zb, X );
8139       gp_Ax3 fromSideAx( Ps1, Zs, X );
8140       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8141       // set trsf
8142       gp_Trsf toBordSys, fromSide2Sys;
8143       toBordSys.SetTransformation( toBordAx );
8144       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8145       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8146
8147       // move
8148       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8149         const SMDS_MeshNode* n = *nBordIt;
8150         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8151         toBordSys.Transforms( xyz );
8152         fromSide2Sys.Transforms( xyz );
8153         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8154       }
8155     }
8156     else {
8157       // just insert nodes XYZ in the nBordXYZ map
8158       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8159         const SMDS_MeshNode* n = *nBordIt;
8160         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8161       }
8162     }
8163
8164     // 2. On the side 2, find the links most co-directed with the correspondent
8165     //    links of the free border
8166
8167     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8168     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8169     sideNodes.push_back( theSideFirstNode );
8170
8171     bool hasVolumes = false;
8172     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8173     set<long> foundSideLinkIDs, checkedLinkIDs;
8174     SMDS_VolumeTool volume;
8175     //const SMDS_MeshNode* faceNodes[ 4 ];
8176
8177     const SMDS_MeshNode*    sideNode;
8178     const SMDS_MeshElement* sideElem;
8179     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8180     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8181     nBordIt = bordNodes.begin();
8182     nBordIt++;
8183     // border node position and border link direction to compare with
8184     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8185     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8186     // choose next side node by link direction or by closeness to
8187     // the current border node:
8188     bool searchByDir = ( *nBordIt != theBordLastNode );
8189     do {
8190       // find the next node on the Side 2
8191       sideNode = 0;
8192       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8193       long linkID;
8194       checkedLinkIDs.clear();
8195       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8196
8197       // loop on inverse elements of current node (prevSideNode) on the Side 2
8198       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8199       while ( invElemIt->more() )
8200       {
8201         const SMDS_MeshElement* elem = invElemIt->next();
8202         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8203         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8204         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8205         bool isVolume = volume.Set( elem );
8206         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8207         if ( isVolume ) // --volume
8208           hasVolumes = true;
8209         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8210           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8211           if(elem->IsQuadratic()) {
8212             const SMDS_VtkFace* F =
8213               dynamic_cast<const SMDS_VtkFace*>(elem);
8214             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8215             // use special nodes iterator
8216             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8217             while( anIter->more() ) {
8218               nodes[ iNode ] = cast2Node(anIter->next());
8219               if ( nodes[ iNode++ ] == prevSideNode )
8220                 iPrevNode = iNode - 1;
8221             }
8222           }
8223           else {
8224             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8225             while ( nIt->more() ) {
8226               nodes[ iNode ] = cast2Node( nIt->next() );
8227               if ( nodes[ iNode++ ] == prevSideNode )
8228                 iPrevNode = iNode - 1;
8229             }
8230           }
8231           // there are 2 links to check
8232           nbNodes = 2;
8233         }
8234         else // --edge
8235           continue;
8236         // loop on links, to be precise, on the second node of links
8237         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8238           const SMDS_MeshNode* n = nodes[ iNode ];
8239           if ( isVolume ) {
8240             if ( !volume.IsLinked( n, prevSideNode ))
8241               continue;
8242           }
8243           else {
8244             if ( iNode ) // a node before prevSideNode
8245               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8246             else         // a node after prevSideNode
8247               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8248           }
8249           // check if this link was already used
8250           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8251           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8252           if (!isJustChecked &&
8253               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8254           {
8255             // test a link geometrically
8256             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8257             bool linkIsBetter = false;
8258             double dot = 0.0, dist = 0.0;
8259             if ( searchByDir ) { // choose most co-directed link
8260               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8261               linkIsBetter = ( dot > maxDot );
8262             }
8263             else { // choose link with the node closest to bordPos
8264               dist = ( nextXYZ - bordPos ).SquareModulus();
8265               linkIsBetter = ( dist < minDist );
8266             }
8267             if ( linkIsBetter ) {
8268               maxDot = dot;
8269               minDist = dist;
8270               linkID = iLink;
8271               sideNode = n;
8272               sideElem = elem;
8273             }
8274           }
8275         }
8276       } // loop on inverse elements of prevSideNode
8277
8278       if ( !sideNode ) {
8279         MESSAGE(" Cant find path by links of the Side 2 ");
8280         return SEW_BAD_SIDE_NODES;
8281       }
8282       sideNodes.push_back( sideNode );
8283       sideElems.push_back( sideElem );
8284       foundSideLinkIDs.insert ( linkID );
8285       prevSideNode = sideNode;
8286
8287       if ( *nBordIt == theBordLastNode )
8288         searchByDir = false;
8289       else {
8290         // find the next border link to compare with
8291         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8292         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8293         // move to next border node if sideNode is before forward border node (bordPos)
8294         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8295           prevBordNode = *nBordIt;
8296           nBordIt++;
8297           bordPos = nBordXYZ[ *nBordIt ];
8298           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8299           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8300         }
8301       }
8302     }
8303     while ( sideNode != theSideSecondNode );
8304
8305     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8306       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8307       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8308     }
8309   } // end nodes search on the side 2
8310
8311   // ============================
8312   // sew the border to the side 2
8313   // ============================
8314
8315   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8316   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8317
8318   TListOfListOfNodes nodeGroupsToMerge;
8319   if ( nbNodes[0] == nbNodes[1] ||
8320        ( theSideIsFreeBorder && !theSideThirdNode)) {
8321
8322     // all nodes are to be merged
8323
8324     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8325          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8326          nIt[0]++, nIt[1]++ )
8327     {
8328       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8329       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8330       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8331     }
8332   }
8333   else {
8334
8335     // insert new nodes into the border and the side to get equal nb of segments
8336
8337     // get normalized parameters of nodes on the borders
8338     //double param[ 2 ][ maxNbNodes ];
8339     double* param[ 2 ];
8340     param[0] = new double [ maxNbNodes ];
8341     param[1] = new double [ maxNbNodes ];
8342     int iNode, iBord;
8343     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8344       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8345       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8346       const SMDS_MeshNode* nPrev = *nIt;
8347       double bordLength = 0;
8348       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8349         const SMDS_MeshNode* nCur = *nIt;
8350         gp_XYZ segment (nCur->X() - nPrev->X(),
8351                         nCur->Y() - nPrev->Y(),
8352                         nCur->Z() - nPrev->Z());
8353         double segmentLen = segment.Modulus();
8354         bordLength += segmentLen;
8355         param[ iBord ][ iNode ] = bordLength;
8356         nPrev = nCur;
8357       }
8358       // normalize within [0,1]
8359       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8360         param[ iBord ][ iNode ] /= bordLength;
8361       }
8362     }
8363
8364     // loop on border segments
8365     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8366     int i[ 2 ] = { 0, 0 };
8367     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8368     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8369
8370     TElemOfNodeListMap insertMap;
8371     TElemOfNodeListMap::iterator insertMapIt;
8372     // insertMap is
8373     // key:   elem to insert nodes into
8374     // value: 2 nodes to insert between + nodes to be inserted
8375     do {
8376       bool next[ 2 ] = { false, false };
8377
8378       // find min adjacent segment length after sewing
8379       double nextParam = 10., prevParam = 0;
8380       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8381         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8382           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8383         if ( i[ iBord ] > 0 )
8384           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8385       }
8386       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8387       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8388       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8389
8390       // choose to insert or to merge nodes
8391       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8392       if ( Abs( du ) <= minSegLen * 0.2 ) {
8393         // merge
8394         // ------
8395         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8396         const SMDS_MeshNode* n0 = *nIt[0];
8397         const SMDS_MeshNode* n1 = *nIt[1];
8398         nodeGroupsToMerge.back().push_back( n1 );
8399         nodeGroupsToMerge.back().push_back( n0 );
8400         // position of node of the border changes due to merge
8401         param[ 0 ][ i[0] ] += du;
8402         // move n1 for the sake of elem shape evaluation during insertion.
8403         // n1 will be removed by MergeNodes() anyway
8404         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8405         next[0] = next[1] = true;
8406       }
8407       else {
8408         // insert
8409         // ------
8410         int intoBord = ( du < 0 ) ? 0 : 1;
8411         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8412         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8413         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8414         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8415         if ( intoBord == 1 ) {
8416           // move node of the border to be on a link of elem of the side
8417           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8418           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8419           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8420           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8421           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8422         }
8423         insertMapIt = insertMap.find( elem );
8424         bool notFound = ( insertMapIt == insertMap.end() );
8425         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8426         if ( otherLink ) {
8427           // insert into another link of the same element:
8428           // 1. perform insertion into the other link of the elem
8429           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8430           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8431           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8432           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8433           // 2. perform insertion into the link of adjacent faces
8434           while (true) {
8435             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8436             if ( adjElem )
8437               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8438             else
8439               break;
8440           }
8441           if (toCreatePolyedrs) {
8442             // perform insertion into the links of adjacent volumes
8443             UpdateVolumes(n12, n22, nodeList);
8444           }
8445           // 3. find an element appeared on n1 and n2 after the insertion
8446           insertMap.erase( elem );
8447           elem = findAdjacentFace( n1, n2, 0 );
8448         }
8449         if ( notFound || otherLink ) {
8450           // add element and nodes of the side into the insertMap
8451           insertMapIt = insertMap.insert
8452             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8453           (*insertMapIt).second.push_back( n1 );
8454           (*insertMapIt).second.push_back( n2 );
8455         }
8456         // add node to be inserted into elem
8457         (*insertMapIt).second.push_back( nIns );
8458         next[ 1 - intoBord ] = true;
8459       }
8460
8461       // go to the next segment
8462       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8463         if ( next[ iBord ] ) {
8464           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8465             eIt[ iBord ]++;
8466           nPrev[ iBord ] = *nIt[ iBord ];
8467           nIt[ iBord ]++; i[ iBord ]++;
8468         }
8469       }
8470     }
8471     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8472
8473     // perform insertion of nodes into elements
8474
8475     for (insertMapIt = insertMap.begin();
8476          insertMapIt != insertMap.end();
8477          insertMapIt++ )
8478     {
8479       const SMDS_MeshElement* elem = (*insertMapIt).first;
8480       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8481       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8482       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8483
8484       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8485
8486       if ( !theSideIsFreeBorder ) {
8487         // look for and insert nodes into the faces adjacent to elem
8488         while (true) {
8489           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8490           if ( adjElem )
8491             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8492           else
8493             break;
8494         }
8495       }
8496       if (toCreatePolyedrs) {
8497         // perform insertion into the links of adjacent volumes
8498         UpdateVolumes(n1, n2, nodeList);
8499       }
8500     }
8501
8502     delete param[0];
8503     delete param[1];
8504   } // end: insert new nodes
8505
8506   MergeNodes ( nodeGroupsToMerge );
8507
8508   return aResult;
8509 }
8510
8511 //=======================================================================
8512 //function : InsertNodesIntoLink
8513 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8514 //           and theBetweenNode2 and split theElement
8515 //=======================================================================
8516
8517 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8518                                            const SMDS_MeshNode*        theBetweenNode1,
8519                                            const SMDS_MeshNode*        theBetweenNode2,
8520                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8521                                            const bool                  toCreatePoly)
8522 {
8523   if ( theFace->GetType() != SMDSAbs_Face ) return;
8524
8525   // find indices of 2 link nodes and of the rest nodes
8526   int iNode = 0, il1, il2, i3, i4;
8527   il1 = il2 = i3 = i4 = -1;
8528   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8529   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8530
8531   if(theFace->IsQuadratic()) {
8532     const SMDS_VtkFace* F =
8533       dynamic_cast<const SMDS_VtkFace*>(theFace);
8534     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8535     // use special nodes iterator
8536     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8537     while( anIter->more() ) {
8538       const SMDS_MeshNode* n = cast2Node(anIter->next());
8539       if ( n == theBetweenNode1 )
8540         il1 = iNode;
8541       else if ( n == theBetweenNode2 )
8542         il2 = iNode;
8543       else if ( i3 < 0 )
8544         i3 = iNode;
8545       else
8546         i4 = iNode;
8547       nodes[ iNode++ ] = n;
8548     }
8549   }
8550   else {
8551     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8552     while ( nodeIt->more() ) {
8553       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8554       if ( n == theBetweenNode1 )
8555         il1 = iNode;
8556       else if ( n == theBetweenNode2 )
8557         il2 = iNode;
8558       else if ( i3 < 0 )
8559         i3 = iNode;
8560       else
8561         i4 = iNode;
8562       nodes[ iNode++ ] = n;
8563     }
8564   }
8565   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8566     return ;
8567
8568   // arrange link nodes to go one after another regarding the face orientation
8569   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8570   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8571   if ( reverse ) {
8572     iNode = il1;
8573     il1 = il2;
8574     il2 = iNode;
8575     aNodesToInsert.reverse();
8576   }
8577   // check that not link nodes of a quadrangles are in good order
8578   int nbFaceNodes = theFace->NbNodes();
8579   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8580     iNode = i3;
8581     i3 = i4;
8582     i4 = iNode;
8583   }
8584
8585   if (toCreatePoly || theFace->IsPoly()) {
8586
8587     iNode = 0;
8588     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8589
8590     // add nodes of face up to first node of link
8591     bool isFLN = false;
8592
8593     if(theFace->IsQuadratic()) {
8594       const SMDS_VtkFace* F =
8595         dynamic_cast<const SMDS_VtkFace*>(theFace);
8596       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8597       // use special nodes iterator
8598       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8599       while( anIter->more()  && !isFLN ) {
8600         const SMDS_MeshNode* n = cast2Node(anIter->next());
8601         poly_nodes[iNode++] = n;
8602         if (n == nodes[il1]) {
8603           isFLN = true;
8604         }
8605       }
8606       // add nodes to insert
8607       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8608       for (; nIt != aNodesToInsert.end(); nIt++) {
8609         poly_nodes[iNode++] = *nIt;
8610       }
8611       // add nodes of face starting from last node of link
8612       while ( anIter->more() ) {
8613         poly_nodes[iNode++] = cast2Node(anIter->next());
8614       }
8615     }
8616     else {
8617       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8618       while ( nodeIt->more() && !isFLN ) {
8619         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8620         poly_nodes[iNode++] = n;
8621         if (n == nodes[il1]) {
8622           isFLN = true;
8623         }
8624       }
8625       // add nodes to insert
8626       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8627       for (; nIt != aNodesToInsert.end(); nIt++) {
8628         poly_nodes[iNode++] = *nIt;
8629       }
8630       // add nodes of face starting from last node of link
8631       while ( nodeIt->more() ) {
8632         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8633         poly_nodes[iNode++] = n;
8634       }
8635     }
8636
8637     // edit or replace the face
8638     SMESHDS_Mesh *aMesh = GetMeshDS();
8639
8640     if (theFace->IsPoly()) {
8641       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8642     }
8643     else {
8644       int aShapeId = FindShape( theFace );
8645
8646       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8647       myLastCreatedElems.Append(newElem);
8648       if ( aShapeId && newElem )
8649         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8650
8651       aMesh->RemoveElement(theFace);
8652     }
8653     return;
8654   }
8655
8656   SMESHDS_Mesh *aMesh = GetMeshDS();
8657   if( !theFace->IsQuadratic() ) {
8658
8659     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8660     int nbLinkNodes = 2 + aNodesToInsert.size();
8661     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8662     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8663     linkNodes[ 0 ] = nodes[ il1 ];
8664     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8665     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8666     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8667       linkNodes[ iNode++ ] = *nIt;
8668     }
8669     // decide how to split a quadrangle: compare possible variants
8670     // and choose which of splits to be a quadrangle
8671     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8672     if ( nbFaceNodes == 3 ) {
8673       iBestQuad = nbSplits;
8674       i4 = i3;
8675     }
8676     else if ( nbFaceNodes == 4 ) {
8677       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8678       double aBestRate = DBL_MAX;
8679       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8680         i1 = 0; i2 = 1;
8681         double aBadRate = 0;
8682         // evaluate elements quality
8683         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8684           if ( iSplit == iQuad ) {
8685             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8686                                    linkNodes[ i2++ ],
8687                                    nodes[ i3 ],
8688                                    nodes[ i4 ]);
8689             aBadRate += getBadRate( &quad, aCrit );
8690           }
8691           else {
8692             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8693                                    linkNodes[ i2++ ],
8694                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8695             aBadRate += getBadRate( &tria, aCrit );
8696           }
8697         }
8698         // choice
8699         if ( aBadRate < aBestRate ) {
8700           iBestQuad = iQuad;
8701           aBestRate = aBadRate;
8702         }
8703       }
8704     }
8705
8706     // create new elements
8707     int aShapeId = FindShape( theFace );
8708
8709     i1 = 0; i2 = 1;
8710     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8711       SMDS_MeshElement* newElem = 0;
8712       if ( iSplit == iBestQuad )
8713         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8714                                   linkNodes[ i2++ ],
8715                                   nodes[ i3 ],
8716                                   nodes[ i4 ]);
8717       else
8718         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8719                                   linkNodes[ i2++ ],
8720                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8721       myLastCreatedElems.Append(newElem);
8722       if ( aShapeId && newElem )
8723         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8724     }
8725
8726     // change nodes of theFace
8727     const SMDS_MeshNode* newNodes[ 4 ];
8728     newNodes[ 0 ] = linkNodes[ i1 ];
8729     newNodes[ 1 ] = linkNodes[ i2 ];
8730     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8731     newNodes[ 3 ] = nodes[ i4 ];
8732     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8733     const SMDS_MeshElement* newElem = 0;
8734     if (iSplit == iBestQuad)
8735       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8736     else
8737       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8738     myLastCreatedElems.Append(newElem);
8739     if ( aShapeId && newElem )
8740       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8741 } // end if(!theFace->IsQuadratic())
8742   else { // theFace is quadratic
8743     // we have to split theFace on simple triangles and one simple quadrangle
8744     int tmp = il1/2;
8745     int nbshift = tmp*2;
8746     // shift nodes in nodes[] by nbshift
8747     int i,j;
8748     for(i=0; i<nbshift; i++) {
8749       const SMDS_MeshNode* n = nodes[0];
8750       for(j=0; j<nbFaceNodes-1; j++) {
8751         nodes[j] = nodes[j+1];
8752       }
8753       nodes[nbFaceNodes-1] = n;
8754     }
8755     il1 = il1 - nbshift;
8756     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8757     //   n0      n1     n2    n0      n1     n2
8758     //     +-----+-----+        +-----+-----+
8759     //      \         /         |           |
8760     //       \       /          |           |
8761     //      n5+     +n3       n7+           +n3
8762     //         \   /            |           |
8763     //          \ /             |           |
8764     //           +              +-----+-----+
8765     //           n4           n6      n5     n4
8766
8767     // create new elements
8768     int aShapeId = FindShape( theFace );
8769
8770     int n1,n2,n3;
8771     if(nbFaceNodes==6) { // quadratic triangle
8772       SMDS_MeshElement* newElem =
8773         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8774       myLastCreatedElems.Append(newElem);
8775       if ( aShapeId && newElem )
8776         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8777       if(theFace->IsMediumNode(nodes[il1])) {
8778         // create quadrangle
8779         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8780         myLastCreatedElems.Append(newElem);
8781         if ( aShapeId && newElem )
8782           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8783         n1 = 1;
8784         n2 = 2;
8785         n3 = 3;
8786       }
8787       else {
8788         // create quadrangle
8789         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8790         myLastCreatedElems.Append(newElem);
8791         if ( aShapeId && newElem )
8792           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8793         n1 = 0;
8794         n2 = 1;
8795         n3 = 5;
8796       }
8797     }
8798     else { // nbFaceNodes==8 - quadratic quadrangle
8799       SMDS_MeshElement* newElem =
8800         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8801       myLastCreatedElems.Append(newElem);
8802       if ( aShapeId && newElem )
8803         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8804       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8805       myLastCreatedElems.Append(newElem);
8806       if ( aShapeId && newElem )
8807         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8808       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8809       myLastCreatedElems.Append(newElem);
8810       if ( aShapeId && newElem )
8811         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8812       if(theFace->IsMediumNode(nodes[il1])) {
8813         // create quadrangle
8814         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8815         myLastCreatedElems.Append(newElem);
8816         if ( aShapeId && newElem )
8817           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8818         n1 = 1;
8819         n2 = 2;
8820         n3 = 3;
8821       }
8822       else {
8823         // create quadrangle
8824         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8825         myLastCreatedElems.Append(newElem);
8826         if ( aShapeId && newElem )
8827           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8828         n1 = 0;
8829         n2 = 1;
8830         n3 = 7;
8831       }
8832     }
8833     // create needed triangles using n1,n2,n3 and inserted nodes
8834     int nbn = 2 + aNodesToInsert.size();
8835     //const SMDS_MeshNode* aNodes[nbn];
8836     vector<const SMDS_MeshNode*> aNodes(nbn);
8837     aNodes[0] = nodes[n1];
8838     aNodes[nbn-1] = nodes[n2];
8839     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8840     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8841       aNodes[iNode++] = *nIt;
8842     }
8843     for(i=1; i<nbn; i++) {
8844       SMDS_MeshElement* newElem =
8845         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8846       myLastCreatedElems.Append(newElem);
8847       if ( aShapeId && newElem )
8848         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8849     }
8850   }
8851   // remove old face
8852   aMesh->RemoveElement(theFace);
8853 }
8854
8855 //=======================================================================
8856 //function : UpdateVolumes
8857 //purpose  :
8858 //=======================================================================
8859 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8860                                       const SMDS_MeshNode*        theBetweenNode2,
8861                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8862 {
8863   myLastCreatedElems.Clear();
8864   myLastCreatedNodes.Clear();
8865
8866   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8867   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8868     const SMDS_MeshElement* elem = invElemIt->next();
8869
8870     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8871     SMDS_VolumeTool aVolume (elem);
8872     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8873       continue;
8874
8875     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8876     int iface, nbFaces = aVolume.NbFaces();
8877     vector<const SMDS_MeshNode *> poly_nodes;
8878     vector<int> quantities (nbFaces);
8879
8880     for (iface = 0; iface < nbFaces; iface++) {
8881       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8882       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8883       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8884
8885       for (int inode = 0; inode < nbFaceNodes; inode++) {
8886         poly_nodes.push_back(faceNodes[inode]);
8887
8888         if (nbInserted == 0) {
8889           if (faceNodes[inode] == theBetweenNode1) {
8890             if (faceNodes[inode + 1] == theBetweenNode2) {
8891               nbInserted = theNodesToInsert.size();
8892
8893               // add nodes to insert
8894               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8895               for (; nIt != theNodesToInsert.end(); nIt++) {
8896                 poly_nodes.push_back(*nIt);
8897               }
8898             }
8899           }
8900           else if (faceNodes[inode] == theBetweenNode2) {
8901             if (faceNodes[inode + 1] == theBetweenNode1) {
8902               nbInserted = theNodesToInsert.size();
8903
8904               // add nodes to insert in reversed order
8905               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8906               nIt--;
8907               for (; nIt != theNodesToInsert.begin(); nIt--) {
8908                 poly_nodes.push_back(*nIt);
8909               }
8910               poly_nodes.push_back(*nIt);
8911             }
8912           }
8913           else {
8914           }
8915         }
8916       }
8917       quantities[iface] = nbFaceNodes + nbInserted;
8918     }
8919
8920     // Replace or update the volume
8921     SMESHDS_Mesh *aMesh = GetMeshDS();
8922
8923     if (elem->IsPoly()) {
8924       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8925
8926     }
8927     else {
8928       int aShapeId = FindShape( elem );
8929
8930       SMDS_MeshElement* newElem =
8931         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8932       myLastCreatedElems.Append(newElem);
8933       if (aShapeId && newElem)
8934         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8935
8936       aMesh->RemoveElement(elem);
8937     }
8938   }
8939 }
8940
8941 namespace
8942 {
8943   //================================================================================
8944   /*!
8945    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8946    */
8947   //================================================================================
8948
8949   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8950                            vector<const SMDS_MeshNode *> & nodes,
8951                            vector<int> &                   nbNodeInFaces )
8952   {
8953     nodes.clear();
8954     nbNodeInFaces.clear();
8955     SMDS_VolumeTool vTool ( elem );
8956     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8957     {
8958       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8959       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8960       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8961     }
8962   }
8963 }
8964
8965 //=======================================================================
8966 /*!
8967  * \brief Convert elements contained in a submesh to quadratic
8968  * \return int - nb of checked elements
8969  */
8970 //=======================================================================
8971
8972 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8973                                              SMESH_MesherHelper& theHelper,
8974                                              const bool          theForce3d)
8975 {
8976   int nbElem = 0;
8977   if( !theSm ) return nbElem;
8978
8979   vector<int> nbNodeInFaces;
8980   vector<const SMDS_MeshNode *> nodes;
8981   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8982   while(ElemItr->more())
8983   {
8984     nbElem++;
8985     const SMDS_MeshElement* elem = ElemItr->next();
8986     if( !elem ) continue;
8987
8988     // analyse a necessity of conversion
8989     const SMDSAbs_ElementType aType = elem->GetType();
8990     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8991       continue;
8992     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8993     bool hasCentralNodes = false;
8994     if ( elem->IsQuadratic() )
8995     {
8996       bool alreadyOK;
8997       switch ( aGeomType ) {
8998       case SMDSEntity_Quad_Triangle:
8999       case SMDSEntity_Quad_Quadrangle:
9000       case SMDSEntity_Quad_Hexa:
9001         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9002
9003       case SMDSEntity_BiQuad_Triangle:
9004       case SMDSEntity_BiQuad_Quadrangle:
9005       case SMDSEntity_TriQuad_Hexa:
9006         alreadyOK = theHelper.GetIsBiQuadratic();
9007         hasCentralNodes = true;
9008         break;
9009       default:
9010         alreadyOK = true;
9011       }
9012       // take into account already present modium nodes
9013       switch ( aType ) {
9014       case SMDSAbs_Volume:
9015         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9016       case SMDSAbs_Face:
9017         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9018       case SMDSAbs_Edge:
9019         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9020       default:;
9021       }
9022       if ( alreadyOK )
9023         continue;
9024     }
9025     // get elem data needed to re-create it
9026     //
9027     const int id      = elem->GetID();
9028     const int nbNodes = elem->NbCornerNodes();
9029     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9030     if ( aGeomType == SMDSEntity_Polyhedra )
9031       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9032     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9033       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9034
9035     // remove a linear element
9036     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9037
9038     // remove central nodes of biquadratic elements (biquad->quad convertion)
9039     if ( hasCentralNodes )
9040       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9041         if ( nodes[i]->NbInverseElements() == 0 )
9042           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9043
9044     const SMDS_MeshElement* NewElem = 0;
9045
9046     switch( aType )
9047     {
9048     case SMDSAbs_Edge :
9049       {
9050         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9051         break;
9052       }
9053     case SMDSAbs_Face :
9054       {
9055         switch(nbNodes)
9056         {
9057         case 3:
9058           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9059           break;
9060         case 4:
9061           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9062           break;
9063         default:
9064           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9065         }
9066         break;
9067       }
9068     case SMDSAbs_Volume :
9069       {
9070         switch( aGeomType )
9071         {
9072         case SMDSEntity_Tetra:
9073           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9074           break;
9075         case SMDSEntity_Pyramid:
9076           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9077           break;
9078         case SMDSEntity_Penta:
9079           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9080           break;
9081         case SMDSEntity_Hexa:
9082         case SMDSEntity_Quad_Hexa:
9083         case SMDSEntity_TriQuad_Hexa:
9084           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9085                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9086           break;
9087         case SMDSEntity_Hexagonal_Prism:
9088         default:
9089           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9090         }
9091         break;
9092       }
9093     default :
9094       continue;
9095     }
9096     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9097     if( NewElem && NewElem->getshapeId() < 1 )
9098       theSm->AddElement( NewElem );
9099   }
9100   return nbElem;
9101 }
9102 //=======================================================================
9103 //function : ConvertToQuadratic
9104 //purpose  :
9105 //=======================================================================
9106
9107 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9108 {
9109   SMESHDS_Mesh* meshDS = GetMeshDS();
9110
9111   SMESH_MesherHelper aHelper(*myMesh);
9112
9113   aHelper.SetIsQuadratic( true );
9114   aHelper.SetIsBiQuadratic( theToBiQuad );
9115   aHelper.SetElementsOnShape(true);
9116   aHelper.ToFixNodeParameters( true );
9117
9118   // convert elements assigned to sub-meshes
9119   int nbCheckedElems = 0;
9120   if ( myMesh->HasShapeToMesh() )
9121   {
9122     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9123     {
9124       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9125       while ( smIt->more() ) {
9126         SMESH_subMesh* sm = smIt->next();
9127         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9128           aHelper.SetSubShape( sm->GetSubShape() );
9129           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9130         }
9131       }
9132     }
9133   }
9134
9135   // convert elements NOT assigned to sub-meshes
9136   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9137   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9138   {
9139     aHelper.SetElementsOnShape(false);
9140     SMESHDS_SubMesh *smDS = 0;
9141
9142     // convert edges
9143     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9144     while( aEdgeItr->more() )
9145     {
9146       const SMDS_MeshEdge* edge = aEdgeItr->next();
9147       if ( !edge->IsQuadratic() )
9148       {
9149         int                  id = edge->GetID();
9150         const SMDS_MeshNode* n1 = edge->GetNode(0);
9151         const SMDS_MeshNode* n2 = edge->GetNode(1);
9152
9153         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9154
9155         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9156         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9157       }
9158       else
9159       {
9160         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9161       }
9162     }
9163
9164     // convert faces
9165     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9166     while( aFaceItr->more() )
9167     {
9168       const SMDS_MeshFace* face = aFaceItr->next();
9169       if ( !face ) continue;
9170       
9171       const SMDSAbs_EntityType type = face->GetEntityType();
9172       bool alreadyOK;
9173       switch( type )
9174       {
9175       case SMDSEntity_Quad_Triangle:
9176       case SMDSEntity_Quad_Quadrangle:
9177         alreadyOK = !theToBiQuad;
9178         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9179         break;
9180       case SMDSEntity_BiQuad_Triangle:
9181       case SMDSEntity_BiQuad_Quadrangle:
9182         alreadyOK = theToBiQuad;
9183         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9184         break;
9185       default: alreadyOK = false;
9186       }
9187       if ( alreadyOK )
9188         continue;
9189
9190       const int id = face->GetID();
9191       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9192
9193       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9194
9195       SMDS_MeshFace * NewFace = 0;
9196       switch( type )
9197       {
9198       case SMDSEntity_Triangle:
9199       case SMDSEntity_Quad_Triangle:
9200       case SMDSEntity_BiQuad_Triangle:
9201         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9202         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9203           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9204         break;
9205
9206       case SMDSEntity_Quadrangle:
9207       case SMDSEntity_Quad_Quadrangle:
9208       case SMDSEntity_BiQuad_Quadrangle:
9209         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9210         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9211           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9212         break;
9213
9214       default:;
9215         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9216       }
9217       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9218     }
9219
9220     // convert volumes
9221     vector<int> nbNodeInFaces;
9222     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9223     while(aVolumeItr->more())
9224     {
9225       const SMDS_MeshVolume* volume = aVolumeItr->next();
9226       if ( !volume ) continue;
9227
9228       const SMDSAbs_EntityType type = volume->GetEntityType();
9229       if ( volume->IsQuadratic() )
9230       {
9231         bool alreadyOK;
9232         switch ( type )
9233         {
9234         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9235         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9236         default:                      alreadyOK = true;
9237         }
9238         if ( alreadyOK )
9239         {
9240           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9241           continue;
9242         }
9243       }
9244       const int id = volume->GetID();
9245       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9246       if ( type == SMDSEntity_Polyhedra )
9247         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9248       else if ( type == SMDSEntity_Hexagonal_Prism )
9249         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9250
9251       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9252
9253       SMDS_MeshVolume * NewVolume = 0;
9254       switch ( type )
9255       {
9256       case SMDSEntity_Tetra:
9257         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9258         break;
9259       case SMDSEntity_Hexa:
9260       case SMDSEntity_Quad_Hexa:
9261       case SMDSEntity_TriQuad_Hexa:
9262         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9263                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9264         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9265           if ( nodes[i]->NbInverseElements() == 0 )
9266             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9267         break;
9268       case SMDSEntity_Pyramid:
9269         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9270                                       nodes[3], nodes[4], id, theForce3d);
9271         break;
9272       case SMDSEntity_Penta:
9273         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9274                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9275         break;
9276       case SMDSEntity_Hexagonal_Prism:
9277       default:
9278         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9279       }
9280       ReplaceElemInGroups(volume, NewVolume, meshDS);
9281     }
9282   }
9283
9284   if ( !theForce3d )
9285   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9286     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9287     // aHelper.FixQuadraticElements(myError);
9288     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9289   }
9290 }
9291
9292 //================================================================================
9293 /*!
9294  * \brief Makes given elements quadratic
9295  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9296  *  \param theElements - elements to make quadratic
9297  */
9298 //================================================================================
9299
9300 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9301                                           TIDSortedElemSet& theElements,
9302                                           const bool        theToBiQuad)
9303 {
9304   if ( theElements.empty() ) return;
9305
9306   // we believe that all theElements are of the same type
9307   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9308
9309   // get all nodes shared by theElements
9310   TIDSortedNodeSet allNodes;
9311   TIDSortedElemSet::iterator eIt = theElements.begin();
9312   for ( ; eIt != theElements.end(); ++eIt )
9313     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9314
9315   // complete theElements with elements of lower dim whose all nodes are in allNodes
9316
9317   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9318   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9319   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9320   for ( ; nIt != allNodes.end(); ++nIt )
9321   {
9322     const SMDS_MeshNode* n = *nIt;
9323     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9324     while ( invIt->more() )
9325     {
9326       const SMDS_MeshElement*      e = invIt->next();
9327       const SMDSAbs_ElementType type = e->GetType();
9328       if ( e->IsQuadratic() )
9329       {
9330         quadAdjacentElems[ type ].insert( e );
9331
9332         bool alreadyOK;
9333         switch ( e->GetEntityType() ) {
9334         case SMDSEntity_Quad_Triangle:
9335         case SMDSEntity_Quad_Quadrangle:
9336         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9337         case SMDSEntity_BiQuad_Triangle:
9338         case SMDSEntity_BiQuad_Quadrangle:
9339         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9340         default:                           alreadyOK = true;
9341         }
9342         if ( alreadyOK )
9343           continue;
9344       }
9345       if ( type >= elemType )
9346         continue; // same type or more complex linear element
9347
9348       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9349         continue; // e is already checked
9350
9351       // check nodes
9352       bool allIn = true;
9353       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9354       while ( nodeIt->more() && allIn )
9355         allIn = allNodes.count( nodeIt->next() );
9356       if ( allIn )
9357         theElements.insert(e );
9358     }
9359   }
9360
9361   SMESH_MesherHelper helper(*myMesh);
9362   helper.SetIsQuadratic( true );
9363   helper.SetIsBiQuadratic( theToBiQuad );
9364
9365   // add links of quadratic adjacent elements to the helper
9366
9367   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9368     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9369           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9370     {
9371       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9372     }
9373   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9374     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9375           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9376     {
9377       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9378     }
9379   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9380     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9381           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9382     {
9383       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9384     }
9385
9386   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9387
9388   SMESHDS_Mesh*  meshDS = GetMeshDS();
9389   SMESHDS_SubMesh* smDS = 0;
9390   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9391   {
9392     const SMDS_MeshElement* elem = *eIt;
9393
9394     bool alreadyOK;
9395     int nbCentralNodes = 0;
9396     switch ( elem->GetEntityType() ) {
9397       // linear convertible
9398     case SMDSEntity_Edge:
9399     case SMDSEntity_Triangle:
9400     case SMDSEntity_Quadrangle:
9401     case SMDSEntity_Tetra:
9402     case SMDSEntity_Pyramid:
9403     case SMDSEntity_Hexa:
9404     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9405       // quadratic that can become bi-quadratic
9406     case SMDSEntity_Quad_Triangle:
9407     case SMDSEntity_Quad_Quadrangle:
9408     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9409       // bi-quadratic
9410     case SMDSEntity_BiQuad_Triangle:
9411     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9412     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9413       // the rest
9414     default:                           alreadyOK = true;
9415     }
9416     if ( alreadyOK ) continue;
9417
9418     const SMDSAbs_ElementType type = elem->GetType();
9419     const int                   id = elem->GetID();
9420     const int              nbNodes = elem->NbCornerNodes();
9421     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9422
9423     helper.SetSubShape( elem->getshapeId() );
9424
9425     if ( !smDS || !smDS->Contains( elem ))
9426       smDS = meshDS->MeshElements( elem->getshapeId() );
9427     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9428
9429     SMDS_MeshElement * newElem = 0;
9430     switch( nbNodes )
9431     {
9432     case 4: // cases for most frequently used element types go first (for optimization)
9433       if ( type == SMDSAbs_Volume )
9434         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9435       else
9436         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9437       break;
9438     case 8:
9439       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9440                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9441       break;
9442     case 3:
9443       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9444       break;
9445     case 2:
9446       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9447       break;
9448     case 5:
9449       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9450                                  nodes[4], id, theForce3d);
9451       break;
9452     case 6:
9453       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9454                                  nodes[4], nodes[5], id, theForce3d);
9455       break;
9456     default:;
9457     }
9458     ReplaceElemInGroups( elem, newElem, meshDS);
9459     if( newElem && smDS )
9460       smDS->AddElement( newElem );
9461
9462      // remove central nodes
9463     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9464       if ( nodes[i]->NbInverseElements() == 0 )
9465         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9466
9467   } // loop on theElements
9468
9469   if ( !theForce3d )
9470   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9471     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9472     // helper.FixQuadraticElements( myError );
9473     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9474   }
9475 }
9476
9477 //=======================================================================
9478 /*!
9479  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9480  * \return int - nb of checked elements
9481  */
9482 //=======================================================================
9483
9484 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9485                                      SMDS_ElemIteratorPtr theItr,
9486                                      const int            theShapeID)
9487 {
9488   int nbElem = 0;
9489   SMESHDS_Mesh* meshDS = GetMeshDS();
9490
9491   while( theItr->more() )
9492   {
9493     const SMDS_MeshElement* elem = theItr->next();
9494     nbElem++;
9495     if( elem && elem->IsQuadratic())
9496     {
9497       int id                    = elem->GetID();
9498       int nbCornerNodes         = elem->NbCornerNodes();
9499       SMDSAbs_ElementType aType = elem->GetType();
9500
9501       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9502
9503       //remove a quadratic element
9504       if ( !theSm || !theSm->Contains( elem ))
9505         theSm = meshDS->MeshElements( elem->getshapeId() );
9506       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9507
9508       // remove medium nodes
9509       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9510         if ( nodes[i]->NbInverseElements() == 0 )
9511           meshDS->RemoveFreeNode( nodes[i], theSm );
9512
9513       // add a linear element
9514       nodes.resize( nbCornerNodes );
9515       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9516       ReplaceElemInGroups(elem, newElem, meshDS);
9517       if( theSm && newElem )
9518         theSm->AddElement( newElem );
9519     }
9520   }
9521   return nbElem;
9522 }
9523
9524 //=======================================================================
9525 //function : ConvertFromQuadratic
9526 //purpose  :
9527 //=======================================================================
9528
9529 bool SMESH_MeshEditor::ConvertFromQuadratic()
9530 {
9531   int nbCheckedElems = 0;
9532   if ( myMesh->HasShapeToMesh() )
9533   {
9534     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9535     {
9536       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9537       while ( smIt->more() ) {
9538         SMESH_subMesh* sm = smIt->next();
9539         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9540           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9541       }
9542     }
9543   }
9544
9545   int totalNbElems =
9546     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9547   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9548   {
9549     SMESHDS_SubMesh *aSM = 0;
9550     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9551   }
9552
9553   return true;
9554 }
9555
9556 namespace
9557 {
9558   //================================================================================
9559   /*!
9560    * \brief Return true if all medium nodes of the element are in the node set
9561    */
9562   //================================================================================
9563
9564   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9565   {
9566     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9567       if ( !nodeSet.count( elem->GetNode(i) ))
9568         return false;
9569     return true;
9570   }
9571 }
9572
9573 //================================================================================
9574 /*!
9575  * \brief Makes given elements linear
9576  */
9577 //================================================================================
9578
9579 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9580 {
9581   if ( theElements.empty() ) return;
9582
9583   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9584   set<int> mediumNodeIDs;
9585   TIDSortedElemSet::iterator eIt = theElements.begin();
9586   for ( ; eIt != theElements.end(); ++eIt )
9587   {
9588     const SMDS_MeshElement* e = *eIt;
9589     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9590       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9591   }
9592
9593   // replace given elements by linear ones
9594   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9595   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9596
9597   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9598   // except those elements sharing medium nodes of quadratic element whose medium nodes
9599   // are not all in mediumNodeIDs
9600
9601   // get remaining medium nodes
9602   TIDSortedNodeSet mediumNodes;
9603   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9604   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9605     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9606       mediumNodes.insert( mediumNodes.end(), n );
9607
9608   // find more quadratic elements to convert
9609   TIDSortedElemSet moreElemsToConvert;
9610   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9611   for ( ; nIt != mediumNodes.end(); ++nIt )
9612   {
9613     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9614     while ( invIt->more() )
9615     {
9616       const SMDS_MeshElement* e = invIt->next();
9617       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9618       {
9619         // find a more complex element including e and
9620         // whose medium nodes are not in mediumNodes
9621         bool complexFound = false;
9622         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9623         {
9624           SMDS_ElemIteratorPtr invIt2 =
9625             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9626           while ( invIt2->more() )
9627           {
9628             const SMDS_MeshElement* eComplex = invIt2->next();
9629             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9630             {
9631               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9632               if ( nbCommonNodes == e->NbNodes())
9633               {
9634                 complexFound = true;
9635                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9636                 break;
9637               }
9638             }
9639           }
9640         }
9641         if ( !complexFound )
9642           moreElemsToConvert.insert( e );
9643       }
9644     }
9645   }
9646   elemIt = elemSetIterator( moreElemsToConvert );
9647   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9648 }
9649
9650 //=======================================================================
9651 //function : SewSideElements
9652 //purpose  :
9653 //=======================================================================
9654
9655 SMESH_MeshEditor::Sew_Error
9656 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9657                                    TIDSortedElemSet&    theSide2,
9658                                    const SMDS_MeshNode* theFirstNode1,
9659                                    const SMDS_MeshNode* theFirstNode2,
9660                                    const SMDS_MeshNode* theSecondNode1,
9661                                    const SMDS_MeshNode* theSecondNode2)
9662 {
9663   myLastCreatedElems.Clear();
9664   myLastCreatedNodes.Clear();
9665
9666   MESSAGE ("::::SewSideElements()");
9667   if ( theSide1.size() != theSide2.size() )
9668     return SEW_DIFF_NB_OF_ELEMENTS;
9669
9670   Sew_Error aResult = SEW_OK;
9671   // Algo:
9672   // 1. Build set of faces representing each side
9673   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9674   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9675
9676   // =======================================================================
9677   // 1. Build set of faces representing each side:
9678   // =======================================================================
9679   // a. build set of nodes belonging to faces
9680   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9681   // c. create temporary faces representing side of volumes if correspondent
9682   //    face does not exist
9683
9684   SMESHDS_Mesh* aMesh = GetMeshDS();
9685   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9686   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9687   TIDSortedElemSet             faceSet1, faceSet2;
9688   set<const SMDS_MeshElement*> volSet1,  volSet2;
9689   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9690   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9691   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9692   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9693   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9694   int iSide, iFace, iNode;
9695
9696   list<const SMDS_MeshElement* > tempFaceList;
9697   for ( iSide = 0; iSide < 2; iSide++ ) {
9698     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9699     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9700     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9701     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9702     set<const SMDS_MeshElement*>::iterator vIt;
9703     TIDSortedElemSet::iterator eIt;
9704     set<const SMDS_MeshNode*>::iterator    nIt;
9705
9706     // check that given nodes belong to given elements
9707     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9708     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9709     int firstIndex = -1, secondIndex = -1;
9710     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9711       const SMDS_MeshElement* elem = *eIt;
9712       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9713       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9714       if ( firstIndex > -1 && secondIndex > -1 ) break;
9715     }
9716     if ( firstIndex < 0 || secondIndex < 0 ) {
9717       // we can simply return until temporary faces created
9718       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9719     }
9720
9721     // -----------------------------------------------------------
9722     // 1a. Collect nodes of existing faces
9723     //     and build set of face nodes in order to detect missing
9724     //     faces corresponding to sides of volumes
9725     // -----------------------------------------------------------
9726
9727     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9728
9729     // loop on the given element of a side
9730     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9731       //const SMDS_MeshElement* elem = *eIt;
9732       const SMDS_MeshElement* elem = *eIt;
9733       if ( elem->GetType() == SMDSAbs_Face ) {
9734         faceSet->insert( elem );
9735         set <const SMDS_MeshNode*> faceNodeSet;
9736         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9737         while ( nodeIt->more() ) {
9738           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9739           nodeSet->insert( n );
9740           faceNodeSet.insert( n );
9741         }
9742         setOfFaceNodeSet.insert( faceNodeSet );
9743       }
9744       else if ( elem->GetType() == SMDSAbs_Volume )
9745         volSet->insert( elem );
9746     }
9747     // ------------------------------------------------------------------------------
9748     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9749     // ------------------------------------------------------------------------------
9750
9751     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9752       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9753       while ( fIt->more() ) { // loop on faces sharing a node
9754         const SMDS_MeshElement* f = fIt->next();
9755         if ( faceSet->find( f ) == faceSet->end() ) {
9756           // check if all nodes are in nodeSet and
9757           // complete setOfFaceNodeSet if they are
9758           set <const SMDS_MeshNode*> faceNodeSet;
9759           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9760           bool allInSet = true;
9761           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9762             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9763             if ( nodeSet->find( n ) == nodeSet->end() )
9764               allInSet = false;
9765             else
9766               faceNodeSet.insert( n );
9767           }
9768           if ( allInSet ) {
9769             faceSet->insert( f );
9770             setOfFaceNodeSet.insert( faceNodeSet );
9771           }
9772         }
9773       }
9774     }
9775
9776     // -------------------------------------------------------------------------
9777     // 1c. Create temporary faces representing sides of volumes if correspondent
9778     //     face does not exist
9779     // -------------------------------------------------------------------------
9780
9781     if ( !volSet->empty() ) {
9782       //int nodeSetSize = nodeSet->size();
9783
9784       // loop on given volumes
9785       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9786         SMDS_VolumeTool vol (*vIt);
9787         // loop on volume faces: find free faces
9788         // --------------------------------------
9789         list<const SMDS_MeshElement* > freeFaceList;
9790         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9791           if ( !vol.IsFreeFace( iFace ))
9792             continue;
9793           // check if there is already a face with same nodes in a face set
9794           const SMDS_MeshElement* aFreeFace = 0;
9795           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9796           int nbNodes = vol.NbFaceNodes( iFace );
9797           set <const SMDS_MeshNode*> faceNodeSet;
9798           vol.GetFaceNodes( iFace, faceNodeSet );
9799           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9800           if ( isNewFace ) {
9801             // no such a face is given but it still can exist, check it
9802             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9803             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9804           }
9805           if ( !aFreeFace ) {
9806             // create a temporary face
9807             if ( nbNodes == 3 ) {
9808               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9809               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9810             }
9811             else if ( nbNodes == 4 ) {
9812               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9813               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9814             }
9815             else {
9816               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9817               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9818               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9819             }
9820             if ( aFreeFace )
9821               tempFaceList.push_back( aFreeFace );
9822           }
9823
9824           if ( aFreeFace )
9825             freeFaceList.push_back( aFreeFace );
9826
9827         } // loop on faces of a volume
9828
9829         // choose one of several free faces of a volume
9830         // --------------------------------------------
9831         if ( freeFaceList.size() > 1 ) {
9832           // choose a face having max nb of nodes shared by other elems of a side
9833           int maxNbNodes = -1;
9834           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9835           while ( fIt != freeFaceList.end() ) { // loop on free faces
9836             int nbSharedNodes = 0;
9837             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9838             while ( nodeIt->more() ) { // loop on free face nodes
9839               const SMDS_MeshNode* n =
9840                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9841               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9842               while ( invElemIt->more() ) {
9843                 const SMDS_MeshElement* e = invElemIt->next();
9844                 nbSharedNodes += faceSet->count( e );
9845                 nbSharedNodes += elemSet->count( e );
9846               }
9847             }
9848             if ( nbSharedNodes > maxNbNodes ) {
9849               maxNbNodes = nbSharedNodes;
9850               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9851             }
9852             else if ( nbSharedNodes == maxNbNodes ) {
9853               fIt++;
9854             }
9855             else {
9856               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9857             }
9858           }
9859           if ( freeFaceList.size() > 1 )
9860           {
9861             // could not choose one face, use another way
9862             // choose a face most close to the bary center of the opposite side
9863             gp_XYZ aBC( 0., 0., 0. );
9864             set <const SMDS_MeshNode*> addedNodes;
9865             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9866             eIt = elemSet2->begin();
9867             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9868               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9869               while ( nodeIt->more() ) { // loop on free face nodes
9870                 const SMDS_MeshNode* n =
9871                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9872                 if ( addedNodes.insert( n ).second )
9873                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9874               }
9875             }
9876             aBC /= addedNodes.size();
9877             double minDist = DBL_MAX;
9878             fIt = freeFaceList.begin();
9879             while ( fIt != freeFaceList.end() ) { // loop on free faces
9880               double dist = 0;
9881               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9882               while ( nodeIt->more() ) { // loop on free face nodes
9883                 const SMDS_MeshNode* n =
9884                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9885                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9886                 dist += ( aBC - p ).SquareModulus();
9887               }
9888               if ( dist < minDist ) {
9889                 minDist = dist;
9890                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9891               }
9892               else
9893                 fIt = freeFaceList.erase( fIt++ );
9894             }
9895           }
9896         } // choose one of several free faces of a volume
9897
9898         if ( freeFaceList.size() == 1 ) {
9899           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9900           faceSet->insert( aFreeFace );
9901           // complete a node set with nodes of a found free face
9902           //           for ( iNode = 0; iNode < ; iNode++ )
9903           //             nodeSet->insert( fNodes[ iNode ] );
9904         }
9905
9906       } // loop on volumes of a side
9907
9908       //       // complete a set of faces if new nodes in a nodeSet appeared
9909       //       // ----------------------------------------------------------
9910       //       if ( nodeSetSize != nodeSet->size() ) {
9911       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9912       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9913       //           while ( fIt->more() ) { // loop on faces sharing a node
9914       //             const SMDS_MeshElement* f = fIt->next();
9915       //             if ( faceSet->find( f ) == faceSet->end() ) {
9916       //               // check if all nodes are in nodeSet and
9917       //               // complete setOfFaceNodeSet if they are
9918       //               set <const SMDS_MeshNode*> faceNodeSet;
9919       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9920       //               bool allInSet = true;
9921       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9922       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9923       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9924       //                   allInSet = false;
9925       //                 else
9926       //                   faceNodeSet.insert( n );
9927       //               }
9928       //               if ( allInSet ) {
9929       //                 faceSet->insert( f );
9930       //                 setOfFaceNodeSet.insert( faceNodeSet );
9931       //               }
9932       //             }
9933       //           }
9934       //         }
9935       //       }
9936     } // Create temporary faces, if there are volumes given
9937   } // loop on sides
9938
9939   if ( faceSet1.size() != faceSet2.size() ) {
9940     // delete temporary faces: they are in reverseElements of actual nodes
9941 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9942 //    while ( tmpFaceIt->more() )
9943 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9944 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9945 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9946 //      aMesh->RemoveElement(*tmpFaceIt);
9947     MESSAGE("Diff nb of faces");
9948     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9949   }
9950
9951   // ============================================================
9952   // 2. Find nodes to merge:
9953   //              bind a node to remove to a node to put instead
9954   // ============================================================
9955
9956   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9957   if ( theFirstNode1 != theFirstNode2 )
9958     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9959   if ( theSecondNode1 != theSecondNode2 )
9960     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9961
9962   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9963   set< long > linkIdSet; // links to process
9964   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9965
9966   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9967   list< NLink > linkList[2];
9968   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9969   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9970   // loop on links in linkList; find faces by links and append links
9971   // of the found faces to linkList
9972   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9973   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9974   {
9975     NLink link[] = { *linkIt[0], *linkIt[1] };
9976     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9977     if ( !linkIdSet.count( linkID ) )
9978       continue;
9979
9980     // by links, find faces in the face sets,
9981     // and find indices of link nodes in the found faces;
9982     // in a face set, there is only one or no face sharing a link
9983     // ---------------------------------------------------------------
9984
9985     const SMDS_MeshElement* face[] = { 0, 0 };
9986     vector<const SMDS_MeshNode*> fnodes[2];
9987     int iLinkNode[2][2];
9988     TIDSortedElemSet avoidSet;
9989     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9990       const SMDS_MeshNode* n1 = link[iSide].first;
9991       const SMDS_MeshNode* n2 = link[iSide].second;
9992       //cout << "Side " << iSide << " ";
9993       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9994       // find a face by two link nodes
9995       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9996                                                       *faceSetPtr[ iSide ], avoidSet,
9997                                                       &iLinkNode[iSide][0],
9998                                                       &iLinkNode[iSide][1] );
9999       if ( face[ iSide ])
10000       {
10001         //cout << " F " << face[ iSide]->GetID() <<endl;
10002         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10003         // put face nodes to fnodes
10004         if ( face[ iSide ]->IsQuadratic() )
10005         {
10006           // use interlaced nodes iterator
10007           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10008           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10009           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10010           while ( nIter->more() )
10011             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10012         }
10013         else
10014         {
10015           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10016                                   face[ iSide ]->end_nodes() );
10017         }
10018         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10019       }
10020     }
10021
10022     // check similarity of elements of the sides
10023     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10024       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10025       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10026         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10027       }
10028       else {
10029         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10030       }
10031       break; // do not return because it's necessary to remove tmp faces
10032     }
10033
10034     // set nodes to merge
10035     // -------------------
10036
10037     if ( face[0] && face[1] )  {
10038       const int nbNodes = face[0]->NbNodes();
10039       if ( nbNodes != face[1]->NbNodes() ) {
10040         MESSAGE("Diff nb of face nodes");
10041         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10042         break; // do not return because it s necessary to remove tmp faces
10043       }
10044       bool reverse[] = { false, false }; // order of nodes in the link
10045       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10046         // analyse link orientation in faces
10047         int i1 = iLinkNode[ iSide ][ 0 ];
10048         int i2 = iLinkNode[ iSide ][ 1 ];
10049         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10050       }
10051       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10052       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10053       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10054       {
10055         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10056                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10057       }
10058
10059       // add other links of the faces to linkList
10060       // -----------------------------------------
10061
10062       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10063         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10064         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10065         if ( !iter_isnew.second ) { // already in a set: no need to process
10066           linkIdSet.erase( iter_isnew.first );
10067         }
10068         else // new in set == encountered for the first time: add
10069         {
10070           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10071           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10072           linkList[0].push_back ( NLink( n1, n2 ));
10073           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10074         }
10075       }
10076     } // 2 faces found
10077
10078     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10079       break;
10080
10081   } // loop on link lists
10082
10083   if ( aResult == SEW_OK &&
10084        ( //linkIt[0] != linkList[0].end() ||
10085          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10086     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10087              " " << (faceSetPtr[1]->empty()));
10088     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10089   }
10090
10091   // ====================================================================
10092   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10093   // ====================================================================
10094
10095   // delete temporary faces
10096 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10097 //  while ( tmpFaceIt->more() )
10098 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10099   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10100   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10101     aMesh->RemoveElement(*tmpFaceIt);
10102
10103   if ( aResult != SEW_OK)
10104     return aResult;
10105
10106   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10107   // loop on nodes replacement map
10108   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10109   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10110     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10111       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10112       nodeIDsToRemove.push_back( nToRemove->GetID() );
10113       // loop on elements sharing nToRemove
10114       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10115       while ( invElemIt->more() ) {
10116         const SMDS_MeshElement* e = invElemIt->next();
10117         // get a new suite of nodes: make replacement
10118         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10119         vector< const SMDS_MeshNode*> nodes( nbNodes );
10120         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10121         while ( nIt->more() ) {
10122           const SMDS_MeshNode* n =
10123             static_cast<const SMDS_MeshNode*>( nIt->next() );
10124           nnIt = nReplaceMap.find( n );
10125           if ( nnIt != nReplaceMap.end() ) {
10126             nbReplaced++;
10127             n = (*nnIt).second;
10128           }
10129           nodes[ i++ ] = n;
10130         }
10131         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10132         //         elemIDsToRemove.push_back( e->GetID() );
10133         //       else
10134         if ( nbReplaced )
10135           {
10136             SMDSAbs_ElementType etyp = e->GetType();
10137             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10138             if (newElem)
10139               {
10140                 myLastCreatedElems.Append(newElem);
10141                 AddToSameGroups(newElem, e, aMesh);
10142                 int aShapeId = e->getshapeId();
10143                 if ( aShapeId )
10144                   {
10145                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10146                   }
10147               }
10148             aMesh->RemoveElement(e);
10149           }
10150       }
10151     }
10152
10153   Remove( nodeIDsToRemove, true );
10154
10155   return aResult;
10156 }
10157
10158 //================================================================================
10159 /*!
10160  * \brief Find corresponding nodes in two sets of faces
10161  * \param theSide1 - first face set
10162  * \param theSide2 - second first face
10163  * \param theFirstNode1 - a boundary node of set 1
10164  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10165  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10166  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10167  * \param nReplaceMap - output map of corresponding nodes
10168  * \return bool  - is a success or not
10169  */
10170 //================================================================================
10171
10172 #ifdef _DEBUG_
10173 //#define DEBUG_MATCHING_NODES
10174 #endif
10175
10176 SMESH_MeshEditor::Sew_Error
10177 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10178                                     set<const SMDS_MeshElement*>& theSide2,
10179                                     const SMDS_MeshNode*          theFirstNode1,
10180                                     const SMDS_MeshNode*          theFirstNode2,
10181                                     const SMDS_MeshNode*          theSecondNode1,
10182                                     const SMDS_MeshNode*          theSecondNode2,
10183                                     TNodeNodeMap &                nReplaceMap)
10184 {
10185   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10186
10187   nReplaceMap.clear();
10188   if ( theFirstNode1 != theFirstNode2 )
10189     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10190   if ( theSecondNode1 != theSecondNode2 )
10191     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10192
10193   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10194   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10195
10196   list< NLink > linkList[2];
10197   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10198   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10199
10200   // loop on links in linkList; find faces by links and append links
10201   // of the found faces to linkList
10202   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10203   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10204     NLink link[] = { *linkIt[0], *linkIt[1] };
10205     if ( linkSet.find( link[0] ) == linkSet.end() )
10206       continue;
10207
10208     // by links, find faces in the face sets,
10209     // and find indices of link nodes in the found faces;
10210     // in a face set, there is only one or no face sharing a link
10211     // ---------------------------------------------------------------
10212
10213     const SMDS_MeshElement* face[] = { 0, 0 };
10214     list<const SMDS_MeshNode*> notLinkNodes[2];
10215     //bool reverse[] = { false, false }; // order of notLinkNodes
10216     int nbNodes[2];
10217     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10218     {
10219       const SMDS_MeshNode* n1 = link[iSide].first;
10220       const SMDS_MeshNode* n2 = link[iSide].second;
10221       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10222       set< const SMDS_MeshElement* > facesOfNode1;
10223       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10224       {
10225         // during a loop of the first node, we find all faces around n1,
10226         // during a loop of the second node, we find one face sharing both n1 and n2
10227         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10228         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10229         while ( fIt->more() ) { // loop on faces sharing a node
10230           const SMDS_MeshElement* f = fIt->next();
10231           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10232               ! facesOfNode1.insert( f ).second ) // f encounters twice
10233           {
10234             if ( face[ iSide ] ) {
10235               MESSAGE( "2 faces per link " );
10236               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10237             }
10238             face[ iSide ] = f;
10239             faceSet->erase( f );
10240
10241             // get not link nodes
10242             int nbN = f->NbNodes();
10243             if ( f->IsQuadratic() )
10244               nbN /= 2;
10245             nbNodes[ iSide ] = nbN;
10246             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10247             int i1 = f->GetNodeIndex( n1 );
10248             int i2 = f->GetNodeIndex( n2 );
10249             int iEnd = nbN, iBeg = -1, iDelta = 1;
10250             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10251             if ( reverse ) {
10252               std::swap( iEnd, iBeg ); iDelta = -1;
10253             }
10254             int i = i2;
10255             while ( true ) {
10256               i += iDelta;
10257               if ( i == iEnd ) i = iBeg + iDelta;
10258               if ( i == i1 ) break;
10259               nodes.push_back ( f->GetNode( i ) );
10260             }
10261           }
10262         }
10263       }
10264     }
10265     // check similarity of elements of the sides
10266     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10267       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10268       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10269         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10270       }
10271       else {
10272         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10273       }
10274     }
10275
10276     // set nodes to merge
10277     // -------------------
10278
10279     if ( face[0] && face[1] )  {
10280       if ( nbNodes[0] != nbNodes[1] ) {
10281         MESSAGE("Diff nb of face nodes");
10282         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10283       }
10284 #ifdef DEBUG_MATCHING_NODES
10285       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10286                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10287                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10288 #endif
10289       int nbN = nbNodes[0];
10290       {
10291         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10292         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10293         for ( int i = 0 ; i < nbN - 2; ++i ) {
10294 #ifdef DEBUG_MATCHING_NODES
10295           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10296 #endif
10297           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10298         }
10299       }
10300
10301       // add other links of the face 1 to linkList
10302       // -----------------------------------------
10303
10304       const SMDS_MeshElement* f0 = face[0];
10305       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10306       for ( int i = 0; i < nbN; i++ )
10307       {
10308         const SMDS_MeshNode* n2 = f0->GetNode( i );
10309         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10310           linkSet.insert( SMESH_TLink( n1, n2 ));
10311         if ( !iter_isnew.second ) { // already in a set: no need to process
10312           linkSet.erase( iter_isnew.first );
10313         }
10314         else // new in set == encountered for the first time: add
10315         {
10316 #ifdef DEBUG_MATCHING_NODES
10317           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10318                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10319 #endif
10320           linkList[0].push_back ( NLink( n1, n2 ));
10321           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10322         }
10323         n1 = n2;
10324       }
10325     } // 2 faces found
10326   } // loop on link lists
10327
10328   return SEW_OK;
10329 }
10330
10331 //================================================================================
10332 /*!
10333  * \brief Create elements equal (on same nodes) to given ones
10334  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10335  *              elements of the uppest dimension are duplicated.
10336  */
10337 //================================================================================
10338
10339 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10340 {
10341   ClearLastCreated();
10342   SMESHDS_Mesh* mesh = GetMeshDS();
10343
10344   // get an element type and an iterator over elements
10345
10346   SMDSAbs_ElementType type;
10347   SMDS_ElemIteratorPtr elemIt;
10348   vector< const SMDS_MeshElement* > allElems;
10349   if ( theElements.empty() )
10350   {
10351     if ( mesh->NbNodes() == 0 )
10352       return;
10353     // get most complex type
10354     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10355       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10356       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10357     };
10358     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10359       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10360       {
10361         type = types[i];
10362         break;
10363       }
10364     // put all elements in the vector <allElems>
10365     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10366     elemIt = mesh->elementsIterator( type );
10367     while ( elemIt->more() )
10368       allElems.push_back( elemIt->next());
10369     elemIt = elemSetIterator( allElems );
10370   }
10371   else
10372   {
10373     type = (*theElements.begin())->GetType();
10374     elemIt = elemSetIterator( theElements );
10375   }
10376
10377   // duplicate elements
10378
10379   if ( type == SMDSAbs_Ball )
10380   {
10381     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10382     while ( elemIt->more() )
10383     {
10384       const SMDS_MeshElement* elem = elemIt->next();
10385       if ( elem->GetType() != SMDSAbs_Ball )
10386         continue;
10387       if (( elem = mesh->AddBall( elem->GetNode(0),
10388                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10389         myLastCreatedElems.Append( elem );
10390     }
10391   }
10392   else
10393   {
10394     vector< const SMDS_MeshNode* > nodes;
10395     while ( elemIt->more() )
10396     {
10397       const SMDS_MeshElement* elem = elemIt->next();
10398       if ( elem->GetType() != type )
10399         continue;
10400
10401       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10402
10403       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10404       {
10405         std::vector<int> quantities =
10406           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10407         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10408       }
10409       else
10410       {
10411         AddElement( nodes, type, elem->IsPoly() );
10412         elem = 0; // myLastCreatedElems is already filled
10413       }
10414       if ( elem )
10415         myLastCreatedElems.Append( elem );
10416     }
10417   }
10418 }
10419
10420 //================================================================================
10421 /*!
10422   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10423   \param theElems - the list of elements (edges or faces) to be replicated
10424   The nodes for duplication could be found from these elements
10425   \param theNodesNot - list of nodes to NOT replicate
10426   \param theAffectedElems - the list of elements (cells and edges) to which the
10427   replicated nodes should be associated to.
10428   \return TRUE if operation has been completed successfully, FALSE otherwise
10429 */
10430 //================================================================================
10431
10432 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10433                                     const TIDSortedElemSet& theNodesNot,
10434                                     const TIDSortedElemSet& theAffectedElems )
10435 {
10436   myLastCreatedElems.Clear();
10437   myLastCreatedNodes.Clear();
10438
10439   if ( theElems.size() == 0 )
10440     return false;
10441
10442   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10443   if ( !aMeshDS )
10444     return false;
10445
10446   bool res = false;
10447   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10448   // duplicate elements and nodes
10449   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10450   // replce nodes by duplications
10451   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10452   return res;
10453 }
10454
10455 //================================================================================
10456 /*!
10457   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10458   \param theMeshDS - mesh instance
10459   \param theElems - the elements replicated or modified (nodes should be changed)
10460   \param theNodesNot - nodes to NOT replicate
10461   \param theNodeNodeMap - relation of old node to new created node
10462   \param theIsDoubleElem - flag os to replicate element or modify
10463   \return TRUE if operation has been completed successfully, FALSE otherwise
10464 */
10465 //================================================================================
10466
10467 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10468                                     const TIDSortedElemSet& theElems,
10469                                     const TIDSortedElemSet& theNodesNot,
10470                                     std::map< const SMDS_MeshNode*,
10471                                     const SMDS_MeshNode* >& theNodeNodeMap,
10472                                     const bool theIsDoubleElem )
10473 {
10474   MESSAGE("doubleNodes");
10475   // iterate on through element and duplicate them (by nodes duplication)
10476   bool res = false;
10477   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10478   for ( ;  elemItr != theElems.end(); ++elemItr )
10479   {
10480     const SMDS_MeshElement* anElem = *elemItr;
10481     if (!anElem)
10482       continue;
10483
10484     bool isDuplicate = false;
10485     // duplicate nodes to duplicate element
10486     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10487     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10488     int ind = 0;
10489     while ( anIter->more() )
10490     {
10491
10492       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10493       SMDS_MeshNode* aNewNode = aCurrNode;
10494       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10495         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10496       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10497       {
10498         // duplicate node
10499         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10500         copyPosition( aCurrNode, aNewNode );
10501         theNodeNodeMap[ aCurrNode ] = aNewNode;
10502         myLastCreatedNodes.Append( aNewNode );
10503       }
10504       isDuplicate |= (aCurrNode != aNewNode);
10505       newNodes[ ind++ ] = aNewNode;
10506     }
10507     if ( !isDuplicate )
10508       continue;
10509
10510     if ( theIsDoubleElem )
10511       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10512     else
10513       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10514
10515     res = true;
10516   }
10517   return res;
10518 }
10519
10520 //================================================================================
10521 /*!
10522   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10523   \param theNodes - identifiers of nodes to be doubled
10524   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10525   nodes. If list of element identifiers is empty then nodes are doubled but
10526   they not assigned to elements
10527   \return TRUE if operation has been completed successfully, FALSE otherwise
10528 */
10529 //================================================================================
10530
10531 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10532                                     const std::list< int >& theListOfModifiedElems )
10533 {
10534   MESSAGE("DoubleNodes");
10535   myLastCreatedElems.Clear();
10536   myLastCreatedNodes.Clear();
10537
10538   if ( theListOfNodes.size() == 0 )
10539     return false;
10540
10541   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10542   if ( !aMeshDS )
10543     return false;
10544
10545   // iterate through nodes and duplicate them
10546
10547   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10548
10549   std::list< int >::const_iterator aNodeIter;
10550   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10551   {
10552     int aCurr = *aNodeIter;
10553     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10554     if ( !aNode )
10555       continue;
10556
10557     // duplicate node
10558
10559     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10560     if ( aNewNode )
10561     {
10562       copyPosition( aNode, aNewNode );
10563       anOldNodeToNewNode[ aNode ] = aNewNode;
10564       myLastCreatedNodes.Append( aNewNode );
10565     }
10566   }
10567
10568   // Create map of new nodes for modified elements
10569
10570   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10571
10572   std::list< int >::const_iterator anElemIter;
10573   for ( anElemIter = theListOfModifiedElems.begin();
10574         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10575   {
10576     int aCurr = *anElemIter;
10577     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10578     if ( !anElem )
10579       continue;
10580
10581     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10582
10583     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10584     int ind = 0;
10585     while ( anIter->more() )
10586     {
10587       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10588       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10589       {
10590         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10591         aNodeArr[ ind++ ] = aNewNode;
10592       }
10593       else
10594         aNodeArr[ ind++ ] = aCurrNode;
10595     }
10596     anElemToNodes[ anElem ] = aNodeArr;
10597   }
10598
10599   // Change nodes of elements
10600
10601   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10602     anElemToNodesIter = anElemToNodes.begin();
10603   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10604   {
10605     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10606     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10607     if ( anElem )
10608       {
10609       MESSAGE("ChangeElementNodes");
10610       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10611       }
10612   }
10613
10614   return true;
10615 }
10616
10617 namespace {
10618
10619   //================================================================================
10620   /*!
10621   \brief Check if element located inside shape
10622   \return TRUE if IN or ON shape, FALSE otherwise
10623   */
10624   //================================================================================
10625
10626   template<class Classifier>
10627   bool isInside(const SMDS_MeshElement* theElem,
10628                 Classifier&             theClassifier,
10629                 const double            theTol)
10630   {
10631     gp_XYZ centerXYZ (0, 0, 0);
10632     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10633     while (aNodeItr->more())
10634       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10635
10636     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10637     theClassifier.Perform(aPnt, theTol);
10638     TopAbs_State aState = theClassifier.State();
10639     return (aState == TopAbs_IN || aState == TopAbs_ON );
10640   }
10641
10642   //================================================================================
10643   /*!
10644    * \brief Classifier of the 3D point on the TopoDS_Face
10645    *        with interaface suitable for isInside()
10646    */
10647   //================================================================================
10648
10649   struct _FaceClassifier
10650   {
10651     Extrema_ExtPS       _extremum;
10652     BRepAdaptor_Surface _surface;
10653     TopAbs_State        _state;
10654
10655     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10656     {
10657       _extremum.Initialize( _surface,
10658                             _surface.FirstUParameter(), _surface.LastUParameter(),
10659                             _surface.FirstVParameter(), _surface.LastVParameter(),
10660                             _surface.Tolerance(), _surface.Tolerance() );
10661     }
10662     void Perform(const gp_Pnt& aPnt, double theTol)
10663     {
10664       theTol *= theTol;
10665       _state = TopAbs_OUT;
10666       _extremum.Perform(aPnt);
10667       if ( _extremum.IsDone() )
10668         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10669           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10670     }
10671     TopAbs_State State() const
10672     {
10673       return _state;
10674     }
10675   };
10676 }
10677
10678 //================================================================================
10679 /*!
10680   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10681   This method is the first step of DoubleNodeElemGroupsInRegion.
10682   \param theElems - list of groups of elements (edges or faces) to be replicated
10683   \param theNodesNot - list of groups of nodes not to replicated
10684   \param theShape - shape to detect affected elements (element which geometric center
10685          located on or inside shape). If the shape is null, detection is done on faces orientations
10686          (select elements with a gravity center on the side given by faces normals).
10687          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10688          The replicated nodes should be associated to affected elements.
10689   \return groups of affected elements
10690   \sa DoubleNodeElemGroupsInRegion()
10691  */
10692 //================================================================================
10693
10694 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10695                                                    const TIDSortedElemSet& theNodesNot,
10696                                                    const TopoDS_Shape&     theShape,
10697                                                    TIDSortedElemSet&       theAffectedElems)
10698 {
10699   if ( theShape.IsNull() )
10700   {
10701     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10702     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10703     std::set<const SMDS_MeshElement*> edgesToCheck;
10704     alreadyCheckedNodes.clear();
10705     alreadyCheckedElems.clear();
10706     edgesToCheck.clear();
10707
10708     // --- iterates on elements to be replicated and get elements by back references from their nodes
10709
10710     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10711     int ielem;
10712     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10713     {
10714       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10715       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10716         continue;
10717       gp_XYZ normal;
10718       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10719       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10720       std::set<const SMDS_MeshNode*> nodesElem;
10721       nodesElem.clear();
10722       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10723       while ( nodeItr->more() )
10724       {
10725         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10726         nodesElem.insert(aNode);
10727       }
10728       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10729       for (; nodit != nodesElem.end(); nodit++)
10730       {
10731         MESSAGE("  noeud ");
10732         const SMDS_MeshNode* aNode = *nodit;
10733         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10734           continue;
10735         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10736           continue;
10737         alreadyCheckedNodes.insert(aNode);
10738         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10739         while ( backElemItr->more() )
10740         {
10741           MESSAGE("    backelem ");
10742           const SMDS_MeshElement* curElem = backElemItr->next();
10743           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10744             continue;
10745           if (theElems.find(curElem) != theElems.end())
10746             continue;
10747           alreadyCheckedElems.insert(curElem);
10748           double x=0, y=0, z=0;
10749           int nb = 0;
10750           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10751           while ( nodeItr2->more() )
10752           {
10753             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10754             x += anotherNode->X();
10755             y += anotherNode->Y();
10756             z += anotherNode->Z();
10757             nb++;
10758           }
10759           gp_XYZ p;
10760           p.SetCoord( x/nb -aNode->X(),
10761                       y/nb -aNode->Y(),
10762                       z/nb -aNode->Z() );
10763           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10764           if (normal*p > 0)
10765           {
10766             MESSAGE("    --- inserted")
10767             theAffectedElems.insert( curElem );
10768           }
10769           else if (curElem->GetType() == SMDSAbs_Edge)
10770             edgesToCheck.insert(curElem);
10771         }
10772       }
10773     }
10774     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10775     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10776     for( ; eit != edgesToCheck.end(); eit++)
10777     {
10778       bool onside = true;
10779       const SMDS_MeshElement* anEdge = *eit;
10780       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10781       while ( nodeItr->more() )
10782       {
10783         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10784         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10785         {
10786           onside = false;
10787           break;
10788         }
10789       }
10790       if (onside)
10791       {
10792         MESSAGE("    --- edge onside inserted")
10793         theAffectedElems.insert(anEdge);
10794       }
10795     }
10796   }
10797   else
10798   {
10799     const double aTol = Precision::Confusion();
10800     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10801     auto_ptr<_FaceClassifier>              aFaceClassifier;
10802     if ( theShape.ShapeType() == TopAbs_SOLID )
10803     {
10804       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10805       bsc3d->PerformInfinitePoint(aTol);
10806     }
10807     else if (theShape.ShapeType() == TopAbs_FACE )
10808     {
10809       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10810     }
10811
10812     // iterates on indicated elements and get elements by back references from their nodes
10813     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10814     int ielem;
10815     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10816     {
10817       MESSAGE("element " << ielem++);
10818       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10819       if (!anElem)
10820         continue;
10821       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10822       while ( nodeItr->more() )
10823       {
10824         MESSAGE("  noeud ");
10825         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10826         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10827           continue;
10828         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10829         while ( backElemItr->more() )
10830         {
10831           MESSAGE("    backelem ");
10832           const SMDS_MeshElement* curElem = backElemItr->next();
10833           if ( curElem && theElems.find(curElem) == theElems.end() &&
10834               ( bsc3d.get() ?
10835                 isInside( curElem, *bsc3d, aTol ) :
10836                 isInside( curElem, *aFaceClassifier, aTol )))
10837             theAffectedElems.insert( curElem );
10838         }
10839       }
10840     }
10841   }
10842   return true;
10843 }
10844
10845 //================================================================================
10846 /*!
10847   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10848   \param theElems - group of of elements (edges or faces) to be replicated
10849   \param theNodesNot - group of nodes not to replicate
10850   \param theShape - shape to detect affected elements (element which geometric center
10851   located on or inside shape).
10852   The replicated nodes should be associated to affected elements.
10853   \return TRUE if operation has been completed successfully, FALSE otherwise
10854 */
10855 //================================================================================
10856
10857 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10858                                             const TIDSortedElemSet& theNodesNot,
10859                                             const TopoDS_Shape&     theShape )
10860 {
10861   if ( theShape.IsNull() )
10862     return false;
10863
10864   const double aTol = Precision::Confusion();
10865   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10866   auto_ptr<_FaceClassifier>              aFaceClassifier;
10867   if ( theShape.ShapeType() == TopAbs_SOLID )
10868   {
10869     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10870     bsc3d->PerformInfinitePoint(aTol);
10871   }
10872   else if (theShape.ShapeType() == TopAbs_FACE )
10873   {
10874     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10875   }
10876
10877   // iterates on indicated elements and get elements by back references from their nodes
10878   TIDSortedElemSet anAffected;
10879   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10880   for ( ;  elemItr != theElems.end(); ++elemItr )
10881   {
10882     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10883     if (!anElem)
10884       continue;
10885
10886     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10887     while ( nodeItr->more() )
10888     {
10889       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10890       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10891         continue;
10892       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10893       while ( backElemItr->more() )
10894       {
10895         const SMDS_MeshElement* curElem = backElemItr->next();
10896         if ( curElem && theElems.find(curElem) == theElems.end() &&
10897              ( bsc3d.get() ?
10898                isInside( curElem, *bsc3d, aTol ) :
10899                isInside( curElem, *aFaceClassifier, aTol )))
10900           anAffected.insert( curElem );
10901       }
10902     }
10903   }
10904   return DoubleNodes( theElems, theNodesNot, anAffected );
10905 }
10906
10907 /*!
10908  *  \brief compute an oriented angle between two planes defined by four points.
10909  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10910  *  @param p0 base of the rotation axe
10911  *  @param p1 extremity of the rotation axe
10912  *  @param g1 belongs to the first plane
10913  *  @param g2 belongs to the second plane
10914  */
10915 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10916 {
10917 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10918 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10919 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10920 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10921   gp_Vec vref(p0, p1);
10922   gp_Vec v1(p0, g1);
10923   gp_Vec v2(p0, g2);
10924   gp_Vec n1 = vref.Crossed(v1);
10925   gp_Vec n2 = vref.Crossed(v2);
10926   try {
10927     return n2.AngleWithRef(n1, vref);
10928   }
10929   catch ( Standard_Failure ) {
10930   }
10931   return Max( v1.Magnitude(), v2.Magnitude() );
10932 }
10933
10934 /*!
10935  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10936  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10937  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10938  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10939  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10940  * 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.
10941  * 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.
10942  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10943  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10944  * \param theElems - list of groups of volumes, where a group of volume is a set of
10945  *        SMDS_MeshElements sorted by Id.
10946  * \param createJointElems - if TRUE, create the elements
10947  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10948  *        the boundary between \a theDomains and the rest mesh
10949  * \return TRUE if operation has been completed successfully, FALSE otherwise
10950  */
10951 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10952                                                      bool                                 createJointElems,
10953                                                      bool                                 onAllBoundaries)
10954 {
10955   MESSAGE("----------------------------------------------");
10956   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10957   MESSAGE("----------------------------------------------");
10958
10959   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10960   meshDS->BuildDownWardConnectivity(true);
10961   CHRONO(50);
10962   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10963
10964   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10965   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10966   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10967
10968   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10969   std::map<int,int>celldom; // cell vtkId --> domain
10970   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10971   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10972   faceDomains.clear();
10973   celldom.clear();
10974   cellDomains.clear();
10975   nodeDomains.clear();
10976   std::map<int,int> emptyMap;
10977   std::set<int> emptySet;
10978   emptyMap.clear();
10979
10980   MESSAGE(".. Number of domains :"<<theElems.size());
10981
10982   TIDSortedElemSet theRestDomElems;
10983   const int iRestDom  = -1;
10984   const int idom0     = onAllBoundaries ? iRestDom : 0;
10985   const int nbDomains = theElems.size();
10986
10987   // Check if the domains do not share an element
10988   for (int idom = 0; idom < nbDomains-1; idom++)
10989     {
10990 //       MESSAGE("... Check of domain #" << idom);
10991       const TIDSortedElemSet& domain = theElems[idom];
10992       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10993       for (; elemItr != domain.end(); ++elemItr)
10994         {
10995           const SMDS_MeshElement* anElem = *elemItr;
10996           int idombisdeb = idom + 1 ;
10997           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10998           {
10999             const TIDSortedElemSet& domainbis = theElems[idombis];
11000             if ( domainbis.count(anElem) )
11001             {
11002               MESSAGE(".... Domain #" << idom);
11003               MESSAGE(".... Domain #" << idombis);
11004               throw SALOME_Exception("The domains are not disjoint.");
11005               return false ;
11006             }
11007           }
11008         }
11009     }
11010
11011   for (int idom = 0; idom < nbDomains; idom++)
11012     {
11013
11014       // --- build a map (face to duplicate --> volume to modify)
11015       //     with all the faces shared by 2 domains (group of elements)
11016       //     and corresponding volume of this domain, for each shared face.
11017       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11018
11019       MESSAGE("... Neighbors of domain #" << idom);
11020       const TIDSortedElemSet& domain = theElems[idom];
11021       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11022       for (; elemItr != domain.end(); ++elemItr)
11023         {
11024           const SMDS_MeshElement* anElem = *elemItr;
11025           if (!anElem)
11026             continue;
11027           int vtkId = anElem->getVtkId();
11028           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11029           int neighborsVtkIds[NBMAXNEIGHBORS];
11030           int downIds[NBMAXNEIGHBORS];
11031           unsigned char downTypes[NBMAXNEIGHBORS];
11032           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11033           for (int n = 0; n < nbNeighbors; n++)
11034             {
11035               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11036               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11037               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11038                 {
11039                   bool ok = false ;
11040                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11041                   {
11042                     // MESSAGE("Domain " << idombis);
11043                     const TIDSortedElemSet& domainbis = theElems[idombis];
11044                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11045                   }
11046                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11047                   {
11048                     DownIdType face(downIds[n], downTypes[n]);
11049                     if (!faceDomains[face].count(idom))
11050                       {
11051                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11052                         celldom[vtkId] = idom;
11053                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11054                       }
11055                     if ( !ok )
11056                     {
11057                       theRestDomElems.insert( elem );
11058                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
11059                       celldom[neighborsVtkIds[n]] = iRestDom;
11060                     }
11061                   }
11062                 }
11063             }
11064         }
11065     }
11066
11067   //MESSAGE("Number of shared faces " << faceDomains.size());
11068   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11069
11070   // --- explore the shared faces domain by domain,
11071   //     explore the nodes of the face and see if they belong to a cell in the domain,
11072   //     which has only a node or an edge on the border (not a shared face)
11073
11074   for (int idomain = idom0; idomain < nbDomains; idomain++)
11075     {
11076       //MESSAGE("Domain " << idomain);
11077       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11078       itface = faceDomains.begin();
11079       for (; itface != faceDomains.end(); ++itface)
11080         {
11081           const std::map<int, int>& domvol = itface->second;
11082           if (!domvol.count(idomain))
11083             continue;
11084           DownIdType face = itface->first;
11085           //MESSAGE(" --- face " << face.cellId);
11086           std::set<int> oldNodes;
11087           oldNodes.clear();
11088           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11089           std::set<int>::iterator itn = oldNodes.begin();
11090           for (; itn != oldNodes.end(); ++itn)
11091             {
11092               int oldId = *itn;
11093               //MESSAGE("     node " << oldId);
11094               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11095               for (int i=0; i<l.ncells; i++)
11096                 {
11097                   int vtkId = l.cells[i];
11098                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11099                   if (!domain.count(anElem))
11100                     continue;
11101                   int vtkType = grid->GetCellType(vtkId);
11102                   int downId = grid->CellIdToDownId(vtkId);
11103                   if (downId < 0)
11104                     {
11105                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11106                       continue; // not OK at this stage of the algorithm:
11107                                 //no cells created after BuildDownWardConnectivity
11108                     }
11109                   DownIdType aCell(downId, vtkType);
11110                   cellDomains[aCell][idomain] = vtkId;
11111                   celldom[vtkId] = idomain;
11112                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11113                 }
11114             }
11115         }
11116     }
11117
11118   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11119   //     for each shared face, get the nodes
11120   //     for each node, for each domain of the face, create a clone of the node
11121
11122   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11123   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11124   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11125
11126   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11127   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11128   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11129
11130   MESSAGE(".. Duplication of the nodes");
11131   for (int idomain = idom0; idomain < nbDomains; idomain++)
11132     {
11133       itface = faceDomains.begin();
11134       for (; itface != faceDomains.end(); ++itface)
11135         {
11136           const std::map<int, int>& domvol = itface->second;
11137           if (!domvol.count(idomain))
11138             continue;
11139           DownIdType face = itface->first;
11140           //MESSAGE(" --- face " << face.cellId);
11141           std::set<int> oldNodes;
11142           oldNodes.clear();
11143           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11144           std::set<int>::iterator itn = oldNodes.begin();
11145           for (; itn != oldNodes.end(); ++itn)
11146             {
11147               int oldId = *itn;
11148               if (nodeDomains[oldId].empty())
11149                 {
11150                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11151                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11152                 }
11153               std::map<int, int>::const_iterator itdom = domvol.begin();
11154               for (; itdom != domvol.end(); ++itdom)
11155                 {
11156                   int idom = itdom->first;
11157                   //MESSAGE("         domain " << idom);
11158                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11159                     {
11160                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11161                         {
11162                           vector<int> orderedDoms;
11163                           //MESSAGE("multiple node " << oldId);
11164                           if (mutipleNodes.count(oldId))
11165                             orderedDoms = mutipleNodes[oldId];
11166                           else
11167                             {
11168                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11169                               for (; it != nodeDomains[oldId].end(); ++it)
11170                                 orderedDoms.push_back(it->first);
11171                             }
11172                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11173                           //stringstream txt;
11174                           //for (int i=0; i<orderedDoms.size(); i++)
11175                           //  txt << orderedDoms[i] << " ";
11176                           //MESSAGE("orderedDoms " << txt.str());
11177                           mutipleNodes[oldId] = orderedDoms;
11178                         }
11179                       double *coords = grid->GetPoint(oldId);
11180                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11181                       copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11182                       int newId = newNode->getVtkId();
11183                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11184                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11185                     }
11186                 }
11187             }
11188         }
11189     }
11190
11191   MESSAGE(".. Creation of elements");
11192   for (int idomain = idom0; idomain < nbDomains; idomain++)
11193     {
11194       itface = faceDomains.begin();
11195       for (; itface != faceDomains.end(); ++itface)
11196         {
11197           std::map<int, int> domvol = itface->second;
11198           if (!domvol.count(idomain))
11199             continue;
11200           DownIdType face = itface->first;
11201           //MESSAGE(" --- face " << face.cellId);
11202           std::set<int> oldNodes;
11203           oldNodes.clear();
11204           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11205           int nbMultipleNodes = 0;
11206           std::set<int>::iterator itn = oldNodes.begin();
11207           for (; itn != oldNodes.end(); ++itn)
11208             {
11209               int oldId = *itn;
11210               if (mutipleNodes.count(oldId))
11211                 nbMultipleNodes++;
11212             }
11213           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11214             {
11215               //MESSAGE("multiple Nodes detected on a shared face");
11216               int downId = itface->first.cellId;
11217               unsigned char cellType = itface->first.cellType;
11218               // --- shared edge or shared face ?
11219               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11220                 {
11221                   int nodes[3];
11222                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11223                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11224                     if (mutipleNodes.count(nodes[i]))
11225                       if (!mutipleNodesToFace.count(nodes[i]))
11226                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11227                 }
11228               else // shared face (between two volumes)
11229                 {
11230                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11231                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11232                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11233                   for (int ie =0; ie < nbEdges; ie++)
11234                     {
11235                       int nodes[3];
11236                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11237                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11238                         {
11239                           vector<int> vn0 = mutipleNodes[nodes[0]];
11240                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11241                           vector<int> doms;
11242                           for (int i0 = 0; i0 < vn0.size(); i0++)
11243                             for (int i1 = 0; i1 < vn1.size(); i1++)
11244                               if (vn0[i0] == vn1[i1])
11245                                 doms.push_back(vn0[i0]);
11246                           if (doms.size() >2)
11247                             {
11248                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11249                               double *coords = grid->GetPoint(nodes[0]);
11250                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11251                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11252                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11253                               gp_Pnt gref;
11254                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11255                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11256                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11257                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11258                               for (int id=0; id < doms.size(); id++)
11259                                 {
11260                                   int idom = doms[id];
11261                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11262                                   for (int ivol=0; ivol<nbvol; ivol++)
11263                                     {
11264                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11265                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11266                                       if (domain.count(elem))
11267                                         {
11268                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11269                                           domvol[idom] = svol;
11270                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11271                                           double values[3];
11272                                           vtkIdType npts = 0;
11273                                           vtkIdType* pts = 0;
11274                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11275                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11276                                           if (id ==0)
11277                                             {
11278                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11279                                               angleDom[idom] = 0;
11280                                             }
11281                                           else
11282                                             {
11283                                               gp_Pnt g(values[0], values[1], values[2]);
11284                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11285                                               //MESSAGE("  angle=" << angleDom[idom]);
11286                                             }
11287                                           break;
11288                                         }
11289                                     }
11290                                 }
11291                               map<double, int> sortedDom; // sort domains by angle
11292                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11293                                 sortedDom[ia->second] = ia->first;
11294                               vector<int> vnodes;
11295                               vector<int> vdom;
11296                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11297                                 {
11298                                   vdom.push_back(ib->second);
11299                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11300                                 }
11301                               for (int ino = 0; ino < nbNodes; ino++)
11302                                 vnodes.push_back(nodes[ino]);
11303                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11304                             }
11305                         }
11306                     }
11307                 }
11308             }
11309         }
11310     }
11311
11312   // --- iterate on shared faces (volumes to modify, face to extrude)
11313   //     get node id's of the face (id SMDS = id VTK)
11314   //     create flat element with old and new nodes if requested
11315
11316   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11317   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11318
11319   std::map<int, std::map<long,int> > nodeQuadDomains;
11320   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11321
11322   MESSAGE(".. Creation of elements: simple junction");
11323   if (createJointElems)
11324     {
11325       int idg;
11326       string joints2DName = "joints2D";
11327       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11328       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11329       string joints3DName = "joints3D";
11330       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11331       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11332
11333       itface = faceDomains.begin();
11334       for (; itface != faceDomains.end(); ++itface)
11335         {
11336           DownIdType face = itface->first;
11337           std::set<int> oldNodes;
11338           std::set<int>::iterator itn;
11339           oldNodes.clear();
11340           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11341
11342           std::map<int, int> domvol = itface->second;
11343           std::map<int, int>::iterator itdom = domvol.begin();
11344           int dom1 = itdom->first;
11345           int vtkVolId = itdom->second;
11346           itdom++;
11347           int dom2 = itdom->first;
11348           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11349                                                              nodeQuadDomains);
11350           stringstream grpname;
11351           grpname << "j_";
11352           if (dom1 < dom2)
11353             grpname << dom1 << "_" << dom2;
11354           else
11355             grpname << dom2 << "_" << dom1;
11356           string namegrp = grpname.str();
11357           if (!mapOfJunctionGroups.count(namegrp))
11358             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11359           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11360           if (sgrp)
11361             sgrp->Add(vol->GetID());
11362           if (vol->GetType() == SMDSAbs_Volume)
11363             joints3DGrp->Add(vol->GetID());
11364           else if (vol->GetType() == SMDSAbs_Face)
11365             joints2DGrp->Add(vol->GetID());
11366         }
11367     }
11368
11369   // --- create volumes on multiple domain intersection if requested
11370   //     iterate on mutipleNodesToFace
11371   //     iterate on edgesMultiDomains
11372
11373   MESSAGE(".. Creation of elements: multiple junction");
11374   if (createJointElems)
11375     {
11376       // --- iterate on mutipleNodesToFace
11377
11378       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11379       for (; itn != mutipleNodesToFace.end(); ++itn)
11380         {
11381           int node = itn->first;
11382           vector<int> orderDom = itn->second;
11383           vector<vtkIdType> orderedNodes;
11384           for (int idom = 0; idom <orderDom.size(); idom++)
11385             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11386             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11387
11388             stringstream grpname;
11389             grpname << "m2j_";
11390             grpname << 0 << "_" << 0;
11391             int idg;
11392             string namegrp = grpname.str();
11393             if (!mapOfJunctionGroups.count(namegrp))
11394               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11395             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11396             if (sgrp)
11397               sgrp->Add(face->GetID());
11398         }
11399
11400       // --- iterate on edgesMultiDomains
11401
11402       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11403       for (; ite != edgesMultiDomains.end(); ++ite)
11404         {
11405           vector<int> nodes = ite->first;
11406           vector<int> orderDom = ite->second;
11407           vector<vtkIdType> orderedNodes;
11408           if (nodes.size() == 2)
11409             {
11410               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11411               for (int ino=0; ino < nodes.size(); ino++)
11412                 if (orderDom.size() == 3)
11413                   for (int idom = 0; idom <orderDom.size(); idom++)
11414                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11415                 else
11416                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11417                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11418               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11419
11420               int idg;
11421               string namegrp = "jointsMultiples";
11422               if (!mapOfJunctionGroups.count(namegrp))
11423                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11424               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11425               if (sgrp)
11426                 sgrp->Add(vol->GetID());
11427             }
11428           else
11429             {
11430               INFOS("Quadratic multiple joints not implemented");
11431               // TODO quadratic nodes
11432             }
11433         }
11434     }
11435
11436   // --- list the explicit faces and edges of the mesh that need to be modified,
11437   //     i.e. faces and edges built with one or more duplicated nodes.
11438   //     associate these faces or edges to their corresponding domain.
11439   //     only the first domain found is kept when a face or edge is shared
11440
11441   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11442   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11443   faceOrEdgeDom.clear();
11444   feDom.clear();
11445
11446   MESSAGE(".. Modification of elements");
11447   for (int idomain = idom0; idomain < nbDomains; idomain++)
11448     {
11449       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11450       for (; itnod != nodeDomains.end(); ++itnod)
11451         {
11452           int oldId = itnod->first;
11453           //MESSAGE("     node " << oldId);
11454           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11455           for (int i = 0; i < l.ncells; i++)
11456             {
11457               int vtkId = l.cells[i];
11458               int vtkType = grid->GetCellType(vtkId);
11459               int downId = grid->CellIdToDownId(vtkId);
11460               if (downId < 0)
11461                 continue; // new cells: not to be modified
11462               DownIdType aCell(downId, vtkType);
11463               int volParents[1000];
11464               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11465               for (int j = 0; j < nbvol; j++)
11466                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11467                   if (!feDom.count(vtkId))
11468                     {
11469                       feDom[vtkId] = idomain;
11470                       faceOrEdgeDom[aCell] = emptyMap;
11471                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11472                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11473                       //        << " type " << vtkType << " downId " << downId);
11474                     }
11475             }
11476         }
11477     }
11478
11479   // --- iterate on shared faces (volumes to modify, face to extrude)
11480   //     get node id's of the face
11481   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11482
11483   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11484   for (int m=0; m<3; m++)
11485     {
11486       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11487       itface = (*amap).begin();
11488       for (; itface != (*amap).end(); ++itface)
11489         {
11490           DownIdType face = itface->first;
11491           std::set<int> oldNodes;
11492           std::set<int>::iterator itn;
11493           oldNodes.clear();
11494           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11495           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11496           std::map<int, int> localClonedNodeIds;
11497
11498           std::map<int, int> domvol = itface->second;
11499           std::map<int, int>::iterator itdom = domvol.begin();
11500           for (; itdom != domvol.end(); ++itdom)
11501             {
11502               int idom = itdom->first;
11503               int vtkVolId = itdom->second;
11504               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11505               localClonedNodeIds.clear();
11506               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11507                 {
11508                   int oldId = *itn;
11509                   if (nodeDomains[oldId].count(idom))
11510                     {
11511                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11512                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11513                     }
11514                 }
11515               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11516             }
11517         }
11518     }
11519
11520   // Remove empty groups (issue 0022812)
11521   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11522   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11523   {
11524     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11525       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11526   }
11527
11528   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11529   grid->BuildLinks();
11530
11531   CHRONOSTOP(50);
11532   counters::stats();
11533   return true;
11534 }
11535
11536 /*!
11537  * \brief Double nodes on some external faces and create flat elements.
11538  * Flat elements are mainly used by some types of mechanic calculations.
11539  *
11540  * Each group of the list must be constituted of faces.
11541  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11542  * @param theElems - list of groups of faces, where a group of faces is a set of
11543  * SMDS_MeshElements sorted by Id.
11544  * @return TRUE if operation has been completed successfully, FALSE otherwise
11545  */
11546 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11547 {
11548   MESSAGE("-------------------------------------------------");
11549   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11550   MESSAGE("-------------------------------------------------");
11551
11552   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11553
11554   // --- For each group of faces
11555   //     duplicate the nodes, create a flat element based on the face
11556   //     replace the nodes of the faces by their clones
11557
11558   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11559   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11560   clonedNodes.clear();
11561   intermediateNodes.clear();
11562   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11563   mapOfJunctionGroups.clear();
11564
11565   for (int idom = 0; idom < theElems.size(); idom++)
11566     {
11567       const TIDSortedElemSet& domain = theElems[idom];
11568       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11569       for (; elemItr != domain.end(); ++elemItr)
11570         {
11571           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11572           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11573           if (!aFace)
11574             continue;
11575           // MESSAGE("aFace=" << aFace->GetID());
11576           bool isQuad = aFace->IsQuadratic();
11577           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11578
11579           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11580
11581           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11582           while (nodeIt->more())
11583             {
11584               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11585               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11586               if (isMedium)
11587                 ln2.push_back(node);
11588               else
11589                 ln0.push_back(node);
11590
11591               const SMDS_MeshNode* clone = 0;
11592               if (!clonedNodes.count(node))
11593                 {
11594                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11595                   copyPosition( node, clone );
11596                   clonedNodes[node] = clone;
11597                 }
11598               else
11599                 clone = clonedNodes[node];
11600
11601               if (isMedium)
11602                 ln3.push_back(clone);
11603               else
11604                 ln1.push_back(clone);
11605
11606               const SMDS_MeshNode* inter = 0;
11607               if (isQuad && (!isMedium))
11608                 {
11609                   if (!intermediateNodes.count(node))
11610                     {
11611                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11612                       copyPosition( node, inter );
11613                       intermediateNodes[node] = inter;
11614                     }
11615                   else
11616                     inter = intermediateNodes[node];
11617                   ln4.push_back(inter);
11618                 }
11619             }
11620
11621           // --- extrude the face
11622
11623           vector<const SMDS_MeshNode*> ln;
11624           SMDS_MeshVolume* vol = 0;
11625           vtkIdType aType = aFace->GetVtkType();
11626           switch (aType)
11627           {
11628             case VTK_TRIANGLE:
11629               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11630               // MESSAGE("vol prism " << vol->GetID());
11631               ln.push_back(ln1[0]);
11632               ln.push_back(ln1[1]);
11633               ln.push_back(ln1[2]);
11634               break;
11635             case VTK_QUAD:
11636               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11637               // MESSAGE("vol hexa " << vol->GetID());
11638               ln.push_back(ln1[0]);
11639               ln.push_back(ln1[1]);
11640               ln.push_back(ln1[2]);
11641               ln.push_back(ln1[3]);
11642               break;
11643             case VTK_QUADRATIC_TRIANGLE:
11644               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11645                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11646               // MESSAGE("vol quad prism " << vol->GetID());
11647               ln.push_back(ln1[0]);
11648               ln.push_back(ln1[1]);
11649               ln.push_back(ln1[2]);
11650               ln.push_back(ln3[0]);
11651               ln.push_back(ln3[1]);
11652               ln.push_back(ln3[2]);
11653               break;
11654             case VTK_QUADRATIC_QUAD:
11655 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11656 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11657 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11658               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11659                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11660                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11661               // MESSAGE("vol quad hexa " << vol->GetID());
11662               ln.push_back(ln1[0]);
11663               ln.push_back(ln1[1]);
11664               ln.push_back(ln1[2]);
11665               ln.push_back(ln1[3]);
11666               ln.push_back(ln3[0]);
11667               ln.push_back(ln3[1]);
11668               ln.push_back(ln3[2]);
11669               ln.push_back(ln3[3]);
11670               break;
11671             case VTK_POLYGON:
11672               break;
11673             default:
11674               break;
11675           }
11676
11677           if (vol)
11678             {
11679               stringstream grpname;
11680               grpname << "jf_";
11681               grpname << idom;
11682               int idg;
11683               string namegrp = grpname.str();
11684               if (!mapOfJunctionGroups.count(namegrp))
11685                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11686               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11687               if (sgrp)
11688                 sgrp->Add(vol->GetID());
11689             }
11690
11691           // --- modify the face
11692
11693           aFace->ChangeNodes(&ln[0], ln.size());
11694         }
11695     }
11696   return true;
11697 }
11698
11699 /*!
11700  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11701  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11702  *  groups of faces to remove inside the object, (idem edges).
11703  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11704  */
11705 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11706                                       const TopoDS_Shape& theShape,
11707                                       SMESH_NodeSearcher* theNodeSearcher,
11708                                       const char* groupName,
11709                                       std::vector<double>&   nodesCoords,
11710                                       std::vector<std::vector<int> >& listOfListOfNodes)
11711 {
11712   MESSAGE("--------------------------------");
11713   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11714   MESSAGE("--------------------------------");
11715
11716   // --- zone of volumes to remove is given :
11717   //     1 either by a geom shape (one or more vertices) and a radius,
11718   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11719   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11720   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11721   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11722   //     defined by it's name.
11723
11724   SMESHDS_GroupBase* groupDS = 0;
11725   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11726   while ( groupIt->more() )
11727     {
11728       groupDS = 0;
11729       SMESH_Group * group = groupIt->next();
11730       if ( !group ) continue;
11731       groupDS = group->GetGroupDS();
11732       if ( !groupDS || groupDS->IsEmpty() ) continue;
11733       std::string grpName = group->GetName();
11734       //MESSAGE("grpName=" << grpName);
11735       if (grpName == groupName)
11736         break;
11737       else
11738         groupDS = 0;
11739     }
11740
11741   bool isNodeGroup = false;
11742   bool isNodeCoords = false;
11743   if (groupDS)
11744     {
11745       if (groupDS->GetType() != SMDSAbs_Node)
11746         return;
11747       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11748     }
11749
11750   if (nodesCoords.size() > 0)
11751     isNodeCoords = true; // a list o nodes given by their coordinates
11752   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11753
11754   // --- define groups to build
11755
11756   int idg; // --- group of SMDS volumes
11757   string grpvName = groupName;
11758   grpvName += "_vol";
11759   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11760   if (!grp)
11761     {
11762       MESSAGE("group not created " << grpvName);
11763       return;
11764     }
11765   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11766
11767   int idgs; // --- group of SMDS faces on the skin
11768   string grpsName = groupName;
11769   grpsName += "_skin";
11770   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11771   if (!grps)
11772     {
11773       MESSAGE("group not created " << grpsName);
11774       return;
11775     }
11776   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11777
11778   int idgi; // --- group of SMDS faces internal (several shapes)
11779   string grpiName = groupName;
11780   grpiName += "_internalFaces";
11781   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11782   if (!grpi)
11783     {
11784       MESSAGE("group not created " << grpiName);
11785       return;
11786     }
11787   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11788
11789   int idgei; // --- group of SMDS faces internal (several shapes)
11790   string grpeiName = groupName;
11791   grpeiName += "_internalEdges";
11792   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11793   if (!grpei)
11794     {
11795       MESSAGE("group not created " << grpeiName);
11796       return;
11797     }
11798   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11799
11800   // --- build downward connectivity
11801
11802   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11803   meshDS->BuildDownWardConnectivity(true);
11804   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11805
11806   // --- set of volumes detected inside
11807
11808   std::set<int> setOfInsideVol;
11809   std::set<int> setOfVolToCheck;
11810
11811   std::vector<gp_Pnt> gpnts;
11812   gpnts.clear();
11813
11814   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11815     {
11816       MESSAGE("group of nodes provided");
11817       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11818       while ( elemIt->more() )
11819         {
11820           const SMDS_MeshElement* elem = elemIt->next();
11821           if (!elem)
11822             continue;
11823           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11824           if (!node)
11825             continue;
11826           SMDS_MeshElement* vol = 0;
11827           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11828           while (volItr->more())
11829             {
11830               vol = (SMDS_MeshElement*)volItr->next();
11831               setOfInsideVol.insert(vol->getVtkId());
11832               sgrp->Add(vol->GetID());
11833             }
11834         }
11835     }
11836   else if (isNodeCoords)
11837     {
11838       MESSAGE("list of nodes coordinates provided");
11839       int i = 0;
11840       int k = 0;
11841       while (i < nodesCoords.size()-2)
11842         {
11843           double x = nodesCoords[i++];
11844           double y = nodesCoords[i++];
11845           double z = nodesCoords[i++];
11846           gp_Pnt p = gp_Pnt(x, y ,z);
11847           gpnts.push_back(p);
11848           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11849           k++;
11850         }
11851     }
11852   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11853     {
11854       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11855       TopTools_IndexedMapOfShape vertexMap;
11856       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11857       gp_Pnt p = gp_Pnt(0,0,0);
11858       if (vertexMap.Extent() < 1)
11859         return;
11860
11861       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11862         {
11863           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11864           p = BRep_Tool::Pnt(vertex);
11865           gpnts.push_back(p);
11866           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11867         }
11868     }
11869
11870   if (gpnts.size() > 0)
11871     {
11872       int nodeId = 0;
11873       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11874       if (startNode)
11875         nodeId = startNode->GetID();
11876       MESSAGE("nodeId " << nodeId);
11877
11878       double radius2 = radius*radius;
11879       MESSAGE("radius2 " << radius2);
11880
11881       // --- volumes on start node
11882
11883       setOfVolToCheck.clear();
11884       SMDS_MeshElement* startVol = 0;
11885       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11886       while (volItr->more())
11887         {
11888           startVol = (SMDS_MeshElement*)volItr->next();
11889           setOfVolToCheck.insert(startVol->getVtkId());
11890         }
11891       if (setOfVolToCheck.empty())
11892         {
11893           MESSAGE("No volumes found");
11894           return;
11895         }
11896
11897       // --- starting with central volumes then their neighbors, check if they are inside
11898       //     or outside the domain, until no more new neighbor volume is inside.
11899       //     Fill the group of inside volumes
11900
11901       std::map<int, double> mapOfNodeDistance2;
11902       mapOfNodeDistance2.clear();
11903       std::set<int> setOfOutsideVol;
11904       while (!setOfVolToCheck.empty())
11905         {
11906           std::set<int>::iterator it = setOfVolToCheck.begin();
11907           int vtkId = *it;
11908           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11909           bool volInside = false;
11910           vtkIdType npts = 0;
11911           vtkIdType* pts = 0;
11912           grid->GetCellPoints(vtkId, npts, pts);
11913           for (int i=0; i<npts; i++)
11914             {
11915               double distance2 = 0;
11916               if (mapOfNodeDistance2.count(pts[i]))
11917                 {
11918                   distance2 = mapOfNodeDistance2[pts[i]];
11919                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11920                 }
11921               else
11922                 {
11923                   double *coords = grid->GetPoint(pts[i]);
11924                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11925                   distance2 = 1.E40;
11926                   for (int j=0; j<gpnts.size(); j++)
11927                     {
11928                       double d2 = aPoint.SquareDistance(gpnts[j]);
11929                       if (d2 < distance2)
11930                         {
11931                           distance2 = d2;
11932                           if (distance2 < radius2)
11933                             break;
11934                         }
11935                     }
11936                   mapOfNodeDistance2[pts[i]] = distance2;
11937                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11938                 }
11939               if (distance2 < radius2)
11940                 {
11941                   volInside = true; // one or more nodes inside the domain
11942                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11943                   break;
11944                 }
11945             }
11946           if (volInside)
11947             {
11948               setOfInsideVol.insert(vtkId);
11949               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11950               int neighborsVtkIds[NBMAXNEIGHBORS];
11951               int downIds[NBMAXNEIGHBORS];
11952               unsigned char downTypes[NBMAXNEIGHBORS];
11953               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11954               for (int n = 0; n < nbNeighbors; n++)
11955                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11956                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11957             }
11958           else
11959             {
11960               setOfOutsideVol.insert(vtkId);
11961               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11962             }
11963           setOfVolToCheck.erase(vtkId);
11964         }
11965     }
11966
11967   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11968   //     If yes, add the volume to the inside set
11969
11970   bool addedInside = true;
11971   std::set<int> setOfVolToReCheck;
11972   while (addedInside)
11973     {
11974       MESSAGE(" --------------------------- re check");
11975       addedInside = false;
11976       std::set<int>::iterator itv = setOfInsideVol.begin();
11977       for (; itv != setOfInsideVol.end(); ++itv)
11978         {
11979           int vtkId = *itv;
11980           int neighborsVtkIds[NBMAXNEIGHBORS];
11981           int downIds[NBMAXNEIGHBORS];
11982           unsigned char downTypes[NBMAXNEIGHBORS];
11983           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11984           for (int n = 0; n < nbNeighbors; n++)
11985             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11986               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11987         }
11988       setOfVolToCheck = setOfVolToReCheck;
11989       setOfVolToReCheck.clear();
11990       while  (!setOfVolToCheck.empty())
11991         {
11992           std::set<int>::iterator it = setOfVolToCheck.begin();
11993           int vtkId = *it;
11994           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11995             {
11996               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11997               int countInside = 0;
11998               int neighborsVtkIds[NBMAXNEIGHBORS];
11999               int downIds[NBMAXNEIGHBORS];
12000               unsigned char downTypes[NBMAXNEIGHBORS];
12001               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12002               for (int n = 0; n < nbNeighbors; n++)
12003                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12004                   countInside++;
12005               MESSAGE("countInside " << countInside);
12006               if (countInside > 1)
12007                 {
12008                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12009                   setOfInsideVol.insert(vtkId);
12010                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12011                   addedInside = true;
12012                 }
12013               else
12014                 setOfVolToReCheck.insert(vtkId);
12015             }
12016           setOfVolToCheck.erase(vtkId);
12017         }
12018     }
12019
12020   // --- map of Downward faces at the boundary, inside the global volume
12021   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12022   //     fill group of SMDS faces inside the volume (when several volume shapes)
12023   //     fill group of SMDS faces on the skin of the global volume (if skin)
12024
12025   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12026   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12027   std::set<int>::iterator it = setOfInsideVol.begin();
12028   for (; it != setOfInsideVol.end(); ++it)
12029     {
12030       int vtkId = *it;
12031       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12032       int neighborsVtkIds[NBMAXNEIGHBORS];
12033       int downIds[NBMAXNEIGHBORS];
12034       unsigned char downTypes[NBMAXNEIGHBORS];
12035       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12036       for (int n = 0; n < nbNeighbors; n++)
12037         {
12038           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12039           if (neighborDim == 3)
12040             {
12041               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12042                 {
12043                   DownIdType face(downIds[n], downTypes[n]);
12044                   boundaryFaces[face] = vtkId;
12045                 }
12046               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12047               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12048               if (vtkFaceId >= 0)
12049                 {
12050                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12051                   // find also the smds edges on this face
12052                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12053                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12054                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12055                   for (int i = 0; i < nbEdges; i++)
12056                     {
12057                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12058                       if (vtkEdgeId >= 0)
12059                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12060                     }
12061                 }
12062             }
12063           else if (neighborDim == 2) // skin of the volume
12064             {
12065               DownIdType face(downIds[n], downTypes[n]);
12066               skinFaces[face] = vtkId;
12067               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12068               if (vtkFaceId >= 0)
12069                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12070             }
12071         }
12072     }
12073
12074   // --- identify the edges constituting the wire of each subshape on the skin
12075   //     define polylines with the nodes of edges, equivalent to wires
12076   //     project polylines on subshapes, and partition, to get geom faces
12077
12078   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12079   std::set<int> emptySet;
12080   emptySet.clear();
12081   std::set<int> shapeIds;
12082
12083   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12084   while (itelem->more())
12085     {
12086       const SMDS_MeshElement *elem = itelem->next();
12087       int shapeId = elem->getshapeId();
12088       int vtkId = elem->getVtkId();
12089       if (!shapeIdToVtkIdSet.count(shapeId))
12090         {
12091           shapeIdToVtkIdSet[shapeId] = emptySet;
12092           shapeIds.insert(shapeId);
12093         }
12094       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12095     }
12096
12097   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12098   std::set<DownIdType, DownIdCompare> emptyEdges;
12099   emptyEdges.clear();
12100
12101   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12102   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12103     {
12104       int shapeId = itShape->first;
12105       MESSAGE(" --- Shape ID --- "<< shapeId);
12106       shapeIdToEdges[shapeId] = emptyEdges;
12107
12108       std::vector<int> nodesEdges;
12109
12110       std::set<int>::iterator its = itShape->second.begin();
12111       for (; its != itShape->second.end(); ++its)
12112         {
12113           int vtkId = *its;
12114           MESSAGE("     " << vtkId);
12115           int neighborsVtkIds[NBMAXNEIGHBORS];
12116           int downIds[NBMAXNEIGHBORS];
12117           unsigned char downTypes[NBMAXNEIGHBORS];
12118           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12119           for (int n = 0; n < nbNeighbors; n++)
12120             {
12121               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12122                 continue;
12123               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12124               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12125               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12126                 {
12127                   DownIdType edge(downIds[n], downTypes[n]);
12128                   if (!shapeIdToEdges[shapeId].count(edge))
12129                     {
12130                       shapeIdToEdges[shapeId].insert(edge);
12131                       int vtkNodeId[3];
12132                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12133                       nodesEdges.push_back(vtkNodeId[0]);
12134                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12135                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12136                     }
12137                 }
12138             }
12139         }
12140
12141       std::list<int> order;
12142       order.clear();
12143       if (nodesEdges.size() > 0)
12144         {
12145           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12146           nodesEdges[0] = -1;
12147           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12148           nodesEdges[1] = -1; // do not reuse this edge
12149           bool found = true;
12150           while (found)
12151             {
12152               int nodeTofind = order.back(); // try first to push back
12153               int i = 0;
12154               for (i = 0; i<nodesEdges.size(); i++)
12155                 if (nodesEdges[i] == nodeTofind)
12156                   break;
12157               if (i == nodesEdges.size())
12158                 found = false; // no follower found on back
12159               else
12160                 {
12161                   if (i%2) // odd ==> use the previous one
12162                     if (nodesEdges[i-1] < 0)
12163                       found = false;
12164                     else
12165                       {
12166                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12167                         nodesEdges[i-1] = -1;
12168                       }
12169                   else // even ==> use the next one
12170                     if (nodesEdges[i+1] < 0)
12171                       found = false;
12172                     else
12173                       {
12174                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12175                         nodesEdges[i+1] = -1;
12176                       }
12177                 }
12178               if (found)
12179                 continue;
12180               // try to push front
12181               found = true;
12182               nodeTofind = order.front(); // try to push front
12183               for (i = 0; i<nodesEdges.size(); i++)
12184                 if (nodesEdges[i] == nodeTofind)
12185                   break;
12186               if (i == nodesEdges.size())
12187                 {
12188                   found = false; // no predecessor found on front
12189                   continue;
12190                 }
12191               if (i%2) // odd ==> use the previous one
12192                 if (nodesEdges[i-1] < 0)
12193                   found = false;
12194                 else
12195                   {
12196                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12197                     nodesEdges[i-1] = -1;
12198                   }
12199               else // even ==> use the next one
12200                 if (nodesEdges[i+1] < 0)
12201                   found = false;
12202                 else
12203                   {
12204                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12205                     nodesEdges[i+1] = -1;
12206                   }
12207             }
12208         }
12209
12210
12211       std::vector<int> nodes;
12212       nodes.push_back(shapeId);
12213       std::list<int>::iterator itl = order.begin();
12214       for (; itl != order.end(); itl++)
12215         {
12216           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12217           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12218         }
12219       listOfListOfNodes.push_back(nodes);
12220     }
12221
12222   //     partition geom faces with blocFissure
12223   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12224   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12225
12226   return;
12227 }
12228
12229
12230 //================================================================================
12231 /*!
12232  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12233  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12234  * \return TRUE if operation has been completed successfully, FALSE otherwise
12235  */
12236 //================================================================================
12237
12238 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12239 {
12240   // iterates on volume elements and detect all free faces on them
12241   SMESHDS_Mesh* aMesh = GetMeshDS();
12242   if (!aMesh)
12243     return false;
12244   //bool res = false;
12245   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12246   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12247   while(vIt->more())
12248   {
12249     const SMDS_MeshVolume* volume = vIt->next();
12250     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12251     vTool.SetExternalNormal();
12252     //const bool isPoly = volume->IsPoly();
12253     const int iQuad = volume->IsQuadratic();
12254     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12255     {
12256       if (!vTool.IsFreeFace(iface))
12257         continue;
12258       nbFree++;
12259       vector<const SMDS_MeshNode *> nodes;
12260       int nbFaceNodes = vTool.NbFaceNodes(iface);
12261       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12262       int inode = 0;
12263       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12264         nodes.push_back(faceNodes[inode]);
12265       if (iQuad) { // add medium nodes
12266         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12267           nodes.push_back(faceNodes[inode]);
12268         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12269           nodes.push_back(faceNodes[8]);
12270       }
12271       // add new face based on volume nodes
12272       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12273         nbExisted++;
12274         continue; // face already exsist
12275       }
12276       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12277       nbCreated++;
12278     }
12279   }
12280   return ( nbFree==(nbExisted+nbCreated) );
12281 }
12282
12283 namespace
12284 {
12285   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12286   {
12287     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12288       return n;
12289     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12290   }
12291 }
12292 //================================================================================
12293 /*!
12294  * \brief Creates missing boundary elements
12295  *  \param elements - elements whose boundary is to be checked
12296  *  \param dimension - defines type of boundary elements to create
12297  *  \param group - a group to store created boundary elements in
12298  *  \param targetMesh - a mesh to store created boundary elements in
12299  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12300  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12301  *                                boundary elements will be copied into the targetMesh
12302  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12303  *                                boundary elements will be added into the new group
12304  *  \param aroundElements - if true, elements will be created on boundary of given
12305  *                          elements else, on boundary of the whole mesh.
12306  * \return nb of added boundary elements
12307  */
12308 //================================================================================
12309
12310 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12311                                        Bnd_Dimension           dimension,
12312                                        SMESH_Group*            group/*=0*/,
12313                                        SMESH_Mesh*             targetMesh/*=0*/,
12314                                        bool                    toCopyElements/*=false*/,
12315                                        bool                    toCopyExistingBoundary/*=false*/,
12316                                        bool                    toAddExistingBondary/*= false*/,
12317                                        bool                    aroundElements/*= false*/)
12318 {
12319   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12320   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12321   // hope that all elements are of the same type, do not check them all
12322   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12323     throw SALOME_Exception(LOCALIZED("wrong element type"));
12324
12325   if ( !targetMesh )
12326     toCopyElements = toCopyExistingBoundary = false;
12327
12328   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12329   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12330   int nbAddedBnd = 0;
12331
12332   // editor adding present bnd elements and optionally holding elements to add to the group
12333   SMESH_MeshEditor* presentEditor;
12334   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12335   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12336
12337   SMESH_MesherHelper helper( *myMesh );
12338   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12339   SMDS_VolumeTool vTool;
12340   TIDSortedElemSet avoidSet;
12341   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12342   int inode;
12343
12344   typedef vector<const SMDS_MeshNode*> TConnectivity;
12345
12346   SMDS_ElemIteratorPtr eIt;
12347   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12348   else                  eIt = elemSetIterator( elements );
12349
12350   while (eIt->more())
12351   {
12352     const SMDS_MeshElement* elem = eIt->next();
12353     const int              iQuad = elem->IsQuadratic();
12354
12355     // ------------------------------------------------------------------------------------
12356     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12357     // ------------------------------------------------------------------------------------
12358     vector<const SMDS_MeshElement*> presentBndElems;
12359     vector<TConnectivity>           missingBndElems;
12360     TConnectivity nodes, elemNodes;
12361     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12362     {
12363       vTool.SetExternalNormal();
12364       const SMDS_MeshElement* otherVol = 0;
12365       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12366       {
12367         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12368              ( !aroundElements || elements.count( otherVol )))
12369           continue;
12370         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12371         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12372         if ( missType == SMDSAbs_Edge ) // boundary edges
12373         {
12374           nodes.resize( 2+iQuad );
12375           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12376           {
12377             for ( int j = 0; j < nodes.size(); ++j )
12378               nodes[j] =nn[i+j];
12379             if ( const SMDS_MeshElement* edge =
12380                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12381               presentBndElems.push_back( edge );
12382             else
12383               missingBndElems.push_back( nodes );
12384           }
12385         }
12386         else // boundary face
12387         {
12388           nodes.clear();
12389           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12390             nodes.push_back( nn[inode] ); // add corner nodes
12391           if (iQuad)
12392             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12393               nodes.push_back( nn[inode] ); // add medium nodes
12394           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12395           if ( iCenter > 0 )
12396             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12397
12398           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12399                                                                SMDSAbs_Face, /*noMedium=*/false ))
12400             presentBndElems.push_back( f );
12401           else
12402             missingBndElems.push_back( nodes );
12403
12404           if ( targetMesh != myMesh )
12405           {
12406             // add 1D elements on face boundary to be added to a new mesh
12407             const SMDS_MeshElement* edge;
12408             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12409             {
12410               if ( iQuad )
12411                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12412               else
12413                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12414               if ( edge && avoidSet.insert( edge ).second )
12415                 presentBndElems.push_back( edge );
12416             }
12417           }
12418         }
12419       }
12420     }
12421     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12422     {
12423       avoidSet.clear(), avoidSet.insert( elem );
12424       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12425                         SMDS_MeshElement::iterator() );
12426       elemNodes.push_back( elemNodes[0] );
12427       nodes.resize( 2 + iQuad );
12428       const int nbLinks = elem->NbCornerNodes();
12429       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12430       {
12431         nodes[0] = elemNodes[iN];
12432         nodes[1] = elemNodes[iN+1+iQuad];
12433         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12434           continue; // not free link
12435
12436         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12437         if ( const SMDS_MeshElement* edge =
12438              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12439           presentBndElems.push_back( edge );
12440         else
12441           missingBndElems.push_back( nodes );
12442       }
12443     }
12444
12445     // ---------------------------------
12446     // 2. Add missing boundary elements
12447     // ---------------------------------
12448     if ( targetMesh != myMesh )
12449       // instead of making a map of nodes in this mesh and targetMesh,
12450       // we create nodes with same IDs.
12451       for ( int i = 0; i < missingBndElems.size(); ++i )
12452       {
12453         TConnectivity& srcNodes = missingBndElems[i];
12454         TConnectivity  nodes( srcNodes.size() );
12455         for ( inode = 0; inode < nodes.size(); ++inode )
12456           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12457         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12458                                                                    missType,
12459                                                                    /*noMedium=*/false))
12460           continue;
12461         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12462         ++nbAddedBnd;
12463       }
12464     else
12465       for ( int i = 0; i < missingBndElems.size(); ++i )
12466       {
12467         TConnectivity& nodes = missingBndElems[i];
12468         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12469                                                                    missType,
12470                                                                    /*noMedium=*/false))
12471           continue;
12472         SMDS_MeshElement* elem =
12473           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12474         ++nbAddedBnd;
12475
12476         // try to set a new element to a shape
12477         if ( myMesh->HasShapeToMesh() )
12478         {
12479           bool ok = true;
12480           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12481           const int nbN = nodes.size() / (iQuad+1 );
12482           for ( inode = 0; inode < nbN && ok; ++inode )
12483           {
12484             pair<int, TopAbs_ShapeEnum> i_stype =
12485               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12486             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12487               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12488           }
12489           if ( ok && mediumShapes.size() > 1 )
12490           {
12491             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12492             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12493             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12494             {
12495               if (( ok = ( stype_i->first != stype_i_0.first )))
12496                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12497                                         aMesh->IndexToShape( stype_i_0.second ));
12498             }
12499           }
12500           if ( ok && mediumShapes.begin()->first == missShapeType )
12501             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12502         }
12503       }
12504
12505     // ----------------------------------
12506     // 3. Copy present boundary elements
12507     // ----------------------------------
12508     if ( toCopyExistingBoundary )
12509       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12510       {
12511         const SMDS_MeshElement* e = presentBndElems[i];
12512         TConnectivity nodes( e->NbNodes() );
12513         for ( inode = 0; inode < nodes.size(); ++inode )
12514           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12515         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12516       }
12517     else // store present elements to add them to a group
12518       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12519       {
12520         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12521       }
12522
12523   } // loop on given elements
12524
12525   // ---------------------------------------------
12526   // 4. Fill group with boundary elements
12527   // ---------------------------------------------
12528   if ( group )
12529   {
12530     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12531       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12532         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12533   }
12534   tgtEditor.myLastCreatedElems.Clear();
12535   tgtEditor2.myLastCreatedElems.Clear();
12536
12537   // -----------------------
12538   // 5. Copy given elements
12539   // -----------------------
12540   if ( toCopyElements && targetMesh != myMesh )
12541   {
12542     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12543     else                  eIt = elemSetIterator( elements );
12544     while (eIt->more())
12545     {
12546       const SMDS_MeshElement* elem = eIt->next();
12547       TConnectivity nodes( elem->NbNodes() );
12548       for ( inode = 0; inode < nodes.size(); ++inode )
12549         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12550       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12551
12552       tgtEditor.myLastCreatedElems.Clear();
12553     }
12554   }
12555   return nbAddedBnd;
12556 }
12557
12558 //================================================================================
12559 /*!
12560  * \brief Copy node position and set \a to node on the same geometry
12561  */
12562 //================================================================================
12563
12564 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12565                                      const SMDS_MeshNode* to )
12566 {
12567   if ( !from || !to ) return;
12568
12569   SMDS_PositionPtr pos = from->GetPosition();
12570   if ( !pos || from->getshapeId() < 1 ) return;
12571
12572   switch ( pos->GetTypeOfPosition() )
12573   {
12574   case SMDS_TOP_3DSPACE: break;
12575
12576   case SMDS_TOP_FACE:
12577   {
12578     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12579     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12580                                 fPos->GetUParameter(), fPos->GetVParameter() );
12581     break;
12582   }
12583   case SMDS_TOP_EDGE:
12584   {
12585     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12586     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12587     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12588     break;
12589   }
12590   case SMDS_TOP_VERTEX:
12591   {
12592     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12593     break;
12594   }
12595   case SMDS_TOP_UNSPEC:
12596   default:;
12597   }
12598 }