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