Salome HOME
22833: [CEA 1346] to extrude a group of faces following the normal of each face
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : SMESH_MeshEditor.cxx
24 // Created   : Mon Apr 12 16:10:22 2004
25 // Author    : Edward AGAPOV (eap)
26
27 #include "SMESH_MeshEditor.hxx"
28
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
38
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
41
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
49
50 #include <Basics_OCCTVersion.hxx>
51
52 #include "utilities.h"
53 #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::CrearLastCreated()
135 {
136   myLastCreatedNodes.Clear();
137   myLastCreatedElems.Clear();
138 }
139
140
141 //=======================================================================
142 /*!
143  * \brief Add element
144  */
145 //=======================================================================
146
147 SMDS_MeshElement*
148 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
149                              const SMDSAbs_ElementType            type,
150                              const bool                           isPoly,
151                              const int                            ID,
152                              const double                         ballDiameter)
153 {
154   //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
155   SMDS_MeshElement* e = 0;
156   int nbnode = node.size();
157   SMESHDS_Mesh* mesh = GetMeshDS();
158   switch ( type ) {
159   case SMDSAbs_Face:
160     if ( !isPoly ) {
161       if      (nbnode == 3) {
162         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
163         else           e = mesh->AddFace      (node[0], node[1], node[2] );
164       }
165       else if (nbnode == 4) {
166         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
167         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
168       }
169       else if (nbnode == 6) {
170         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
171                                                node[4], node[5], ID);
172         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
173                                                node[4], node[5] );
174       }
175       else if (nbnode == 7) {
176         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
177                                                node[4], node[5], node[6], ID);
178         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
179                                                node[4], node[5], node[6] );
180       }
181       else if (nbnode == 8) {
182         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
183                                                node[4], node[5], node[6], node[7], ID);
184         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
185                                                node[4], node[5], node[6], node[7] );
186       }
187       else if (nbnode == 9) {
188         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
189                                                node[4], node[5], node[6], node[7], node[8], ID);
190         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
191                                                node[4], node[5], node[6], node[7], node[8] );
192       }
193     } else {
194       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
195       else           e = mesh->AddPolygonalFace      (node    );
196     }
197     break;
198
199   case SMDSAbs_Volume:
200     if ( !isPoly ) {
201       if      (nbnode == 4) {
202         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
203         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
204       }
205       else if (nbnode == 5) {
206         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
207                                                  node[4], ID);
208         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
209                                                  node[4] );
210       }
211       else if (nbnode == 6) {
212         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213                                                  node[4], node[5], ID);
214         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
215                                                  node[4], node[5] );
216       }
217       else if (nbnode == 8) {
218         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
219                                                  node[4], node[5], node[6], node[7], ID);
220         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
221                                                  node[4], node[5], node[6], node[7] );
222       }
223       else if (nbnode == 10) {
224         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
225                                                  node[4], node[5], node[6], node[7],
226                                                  node[8], node[9], ID);
227         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
228                                                  node[4], node[5], node[6], node[7],
229                                                  node[8], node[9] );
230       }
231       else if (nbnode == 12) {
232         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
233                                                  node[4], node[5], node[6], node[7],
234                                                  node[8], node[9], node[10], node[11], ID);
235         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
236                                                  node[4], node[5], node[6], node[7],
237                                                  node[8], node[9], node[10], node[11] );
238       }
239       else if (nbnode == 13) {
240         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
241                                                  node[4], node[5], node[6], node[7],
242                                                  node[8], node[9], node[10],node[11],
243                                                  node[12],ID);
244         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
245                                                  node[4], node[5], node[6], node[7],
246                                                  node[8], node[9], node[10],node[11],
247                                                  node[12] );
248       }
249       else if (nbnode == 15) {
250         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
251                                                  node[4], node[5], node[6], node[7],
252                                                  node[8], node[9], node[10],node[11],
253                                                  node[12],node[13],node[14],ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5], node[6], node[7],
256                                                  node[8], node[9], node[10],node[11],
257                                                  node[12],node[13],node[14] );
258       }
259       else if (nbnode == 20) {
260         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7],
262                                                  node[8], node[9], node[10],node[11],
263                                                  node[12],node[13],node[14],node[15],
264                                                  node[16],node[17],node[18],node[19],ID);
265         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
266                                                  node[4], node[5], node[6], node[7],
267                                                  node[8], node[9], node[10],node[11],
268                                                  node[12],node[13],node[14],node[15],
269                                                  node[16],node[17],node[18],node[19] );
270       }
271       else if (nbnode == 27) {
272         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273                                                  node[4], node[5], node[6], node[7],
274                                                  node[8], node[9], node[10],node[11],
275                                                  node[12],node[13],node[14],node[15],
276                                                  node[16],node[17],node[18],node[19],
277                                                  node[20],node[21],node[22],node[23],
278                                                  node[24],node[25],node[26], ID);
279         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
280                                                  node[4], node[5], node[6], node[7],
281                                                  node[8], node[9], node[10],node[11],
282                                                  node[12],node[13],node[14],node[15],
283                                                  node[16],node[17],node[18],node[19],
284                                                  node[20],node[21],node[22],node[23],
285                                                  node[24],node[25],node[26] );
286       }
287     }
288     break;
289
290   case SMDSAbs_Edge:
291     if ( nbnode == 2 ) {
292       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
293       else           e = mesh->AddEdge      (node[0], node[1] );
294     }
295     else if ( nbnode == 3 ) {
296       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
297       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
298     }
299     break;
300
301   case SMDSAbs_0DElement:
302     if ( nbnode == 1 ) {
303       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
304       else           e = mesh->Add0DElement      (node[0] );
305     }
306     break;
307
308   case SMDSAbs_Node:
309     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
310     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z());
311     break;
312
313   case SMDSAbs_Ball:
314     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
315     else           e = mesh->AddBall      (node[0], ballDiameter);
316     break;
317
318   default:;
319   }
320   if ( e ) myLastCreatedElems.Append( e );
321   return e;
322 }
323
324 //=======================================================================
325 /*!
326  * \brief Add element
327  */
328 //=======================================================================
329
330 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> &       nodeIDs,
331                                                const SMDSAbs_ElementType type,
332                                                const bool                isPoly,
333                                                const int                 ID)
334 {
335   vector<const SMDS_MeshNode*> nodes;
336   nodes.reserve( nodeIDs.size() );
337   vector<int>::const_iterator id = nodeIDs.begin();
338   while ( id != nodeIDs.end() ) {
339     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
340       nodes.push_back( node );
341     else
342       return 0;
343   }
344   return AddElement( nodes, type, isPoly, ID );
345 }
346
347 //=======================================================================
348 //function : Remove
349 //purpose  : Remove a node or an element.
350 //           Modify a compute state of sub-meshes which become empty
351 //=======================================================================
352
353 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
354                               const bool         isNodes )
355 {
356   myLastCreatedElems.Clear();
357   myLastCreatedNodes.Clear();
358
359   SMESHDS_Mesh* aMesh = GetMeshDS();
360   set< SMESH_subMesh *> smmap;
361
362   int removed = 0;
363   list<int>::const_iterator it = theIDs.begin();
364   for ( ; it != theIDs.end(); it++ ) {
365     const SMDS_MeshElement * elem;
366     if ( isNodes )
367       elem = aMesh->FindNode( *it );
368     else
369       elem = aMesh->FindElement( *it );
370     if ( !elem )
371       continue;
372
373     // Notify VERTEX sub-meshes about modification
374     if ( isNodes ) {
375       const SMDS_MeshNode* node = cast2Node( elem );
376       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
377         if ( int aShapeID = node->getshapeId() )
378           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
379             smmap.insert( sm );
380     }
381     // Find sub-meshes to notify about modification
382     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
383     //     while ( nodeIt->more() ) {
384     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
385     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
386     //       if ( aPosition.get() ) {
387     //         if ( int aShapeID = aPosition->GetShapeId() ) {
388     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
389     //             smmap.insert( sm );
390     //         }
391     //       }
392     //     }
393
394     // Do remove
395     if ( isNodes )
396       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
397     else
398       aMesh->RemoveElement( elem );
399     removed++;
400   }
401
402   // Notify sub-meshes about modification
403   if ( !smmap.empty() ) {
404     set< SMESH_subMesh *>::iterator smIt;
405     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
406       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
407   }
408
409   //   // Check if the whole mesh becomes empty
410   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
411   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
412
413   return removed;
414 }
415
416 //================================================================================
417 /*!
418  * \brief Create 0D elements on all nodes of the given object except those
419  *        nodes on which a 0D element already exists.
420  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
421  *                    the all mesh is treated
422  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
423  */
424 //================================================================================
425
426 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
427                                                    TIDSortedElemSet&       all0DElems )
428 {
429   SMDS_ElemIteratorPtr elemIt;
430   vector< const SMDS_MeshElement* > allNodes;
431   if ( elements.empty() )
432   {
433     allNodes.reserve( GetMeshDS()->NbNodes() );
434     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
435     while ( elemIt->more() )
436       allNodes.push_back( elemIt->next() );
437
438     elemIt = elemSetIterator( allNodes );
439   }
440   else
441   {
442     elemIt = elemSetIterator( elements );
443   }
444
445   while ( elemIt->more() )
446   {
447     const SMDS_MeshElement* e = elemIt->next();
448     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
449     while ( nodeIt->more() )
450     {
451       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
452       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
453       if ( it0D->more() )
454         all0DElems.insert( it0D->next() );
455       else {
456         myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
457         all0DElems.insert( myLastCreatedElems.Last() );
458       }
459     }
460   }
461 }
462
463 //=======================================================================
464 //function : FindShape
465 //purpose  : Return an index of the shape theElem is on
466 //           or zero if a shape not found
467 //=======================================================================
468
469 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
470 {
471   myLastCreatedElems.Clear();
472   myLastCreatedNodes.Clear();
473
474   SMESHDS_Mesh * aMesh = GetMeshDS();
475   if ( aMesh->ShapeToMesh().IsNull() )
476     return 0;
477
478   int aShapeID = theElem->getshapeId();
479   if ( aShapeID < 1 )
480     return 0;
481
482   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
483     if ( sm->Contains( theElem ))
484       return aShapeID;
485
486   if ( theElem->GetType() == SMDSAbs_Node ) {
487     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
488   }
489   else {
490     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
491   }
492
493   TopoDS_Shape aShape; // the shape a node of theElem is on
494   if ( theElem->GetType() != SMDSAbs_Node )
495   {
496     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
497     while ( nodeIt->more() ) {
498       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
499       if ((aShapeID = node->getshapeId()) > 0) {
500         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
501           if ( sm->Contains( theElem ))
502             return aShapeID;
503           if ( aShape.IsNull() )
504             aShape = aMesh->IndexToShape( aShapeID );
505         }
506       }
507     }
508   }
509
510   // None of nodes is on a proper shape,
511   // find the shape among ancestors of aShape on which a node is
512   if ( !aShape.IsNull() ) {
513     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
514     for ( ; ancIt.More(); ancIt.Next() ) {
515       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
516       if ( sm && sm->Contains( theElem ))
517         return aMesh->ShapeToIndex( ancIt.Value() );
518     }
519   }
520   else
521   {
522     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
523     while ( const SMESHDS_SubMesh* sm = smIt->next() )
524       if ( sm->Contains( theElem ))
525         return sm->GetID();
526   }
527
528   return 0;
529 }
530
531 //=======================================================================
532 //function : IsMedium
533 //purpose  :
534 //=======================================================================
535
536 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
537                                 const SMDSAbs_ElementType typeToCheck)
538 {
539   bool isMedium = false;
540   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
541   while (it->more() && !isMedium ) {
542     const SMDS_MeshElement* elem = it->next();
543     isMedium = elem->IsMediumNode(node);
544   }
545   return isMedium;
546 }
547
548 //=======================================================================
549 //function : shiftNodesQuadTria
550 //purpose  : Shift nodes in the array corresponded to quadratic triangle
551 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
552 //=======================================================================
553
554 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
555 {
556   const SMDS_MeshNode* nd1 = aNodes[0];
557   aNodes[0] = aNodes[1];
558   aNodes[1] = aNodes[2];
559   aNodes[2] = nd1;
560   const SMDS_MeshNode* nd2 = aNodes[3];
561   aNodes[3] = aNodes[4];
562   aNodes[4] = aNodes[5];
563   aNodes[5] = nd2;
564 }
565
566 //=======================================================================
567 //function : nbEdgeConnectivity
568 //purpose  : return number of the edges connected with the theNode.
569 //           if theEdges has connections with the other type of the
570 //           elements, return -1
571 //=======================================================================
572
573 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
574 {
575   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
576   // int nb=0;
577   // while(elemIt->more()) {
578   //   elemIt->next();
579   //   nb++;
580   // }
581   // return nb;
582   return theNode->NbInverseElements();
583 }
584
585 //=======================================================================
586 //function : getNodesFromTwoTria
587 //purpose  : 
588 //=======================================================================
589
590 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
591                                 const SMDS_MeshElement * theTria2,
592                                 vector< const SMDS_MeshNode*>& N1,
593                                 vector< const SMDS_MeshNode*>& N2)
594 {
595   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
596   if ( N1.size() < 6 ) return false;
597   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
598   if ( N2.size() < 6 ) return false;
599
600   int sames[3] = {-1,-1,-1};
601   int nbsames = 0;
602   int i, j;
603   for(i=0; i<3; i++) {
604     for(j=0; j<3; j++) {
605       if(N1[i]==N2[j]) {
606         sames[i] = j;
607         nbsames++;
608         break;
609       }
610     }
611   }
612   if(nbsames!=2) return false;
613   if(sames[0]>-1) {
614     shiftNodesQuadTria(N1);
615     if(sames[1]>-1) {
616       shiftNodesQuadTria(N1);
617     }
618   }
619   i = sames[0] + sames[1] + sames[2];
620   for(; i<2; i++) {
621     shiftNodesQuadTria(N2);
622   }
623   // now we receive following N1 and N2 (using numeration as in the image below)
624   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
625   // i.e. first nodes from both arrays form a new diagonal
626   return true;
627 }
628
629 //=======================================================================
630 //function : InverseDiag
631 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
632 //           but having other common link.
633 //           Return False if args are improper
634 //=======================================================================
635
636 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
637                                     const SMDS_MeshElement * theTria2 )
638 {
639   MESSAGE("InverseDiag");
640   myLastCreatedElems.Clear();
641   myLastCreatedNodes.Clear();
642
643   if (!theTria1 || !theTria2)
644     return false;
645
646   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
647   if (!F1) return false;
648   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
649   if (!F2) return false;
650   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
651       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
652
653     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
654     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
655     //    |/ |                                         | \|
656     //  B +--+ 2                                     B +--+ 2
657
658     // put nodes in array and find out indices of the same ones
659     const SMDS_MeshNode* aNodes [6];
660     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
661     int i = 0;
662     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
663     while ( it->more() ) {
664       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
665
666       if ( i > 2 ) // theTria2
667         // find same node of theTria1
668         for ( int j = 0; j < 3; j++ )
669           if ( aNodes[ i ] == aNodes[ j ]) {
670             sameInd[ j ] = i;
671             sameInd[ i ] = j;
672             break;
673           }
674       // next
675       i++;
676       if ( i == 3 ) {
677         if ( it->more() )
678           return false; // theTria1 is not a triangle
679         it = theTria2->nodesIterator();
680       }
681       if ( i == 6 && it->more() )
682         return false; // theTria2 is not a triangle
683     }
684
685     // find indices of 1,2 and of A,B in theTria1
686     int iA = -1, iB = 0, i1 = 0, i2 = 0;
687     for ( i = 0; i < 6; i++ ) {
688       if ( sameInd [ i ] == -1 ) {
689         if ( i < 3 ) i1 = i;
690         else         i2 = i;
691       }
692       else if (i < 3) {
693         if ( iA >= 0) iB = i;
694         else          iA = i;
695       }
696     }
697     // nodes 1 and 2 should not be the same
698     if ( aNodes[ i1 ] == aNodes[ i2 ] )
699       return false;
700
701     // theTria1: A->2
702     aNodes[ iA ] = aNodes[ i2 ];
703     // theTria2: B->1
704     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
705
706     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
707     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
708
709     return true;
710
711   } // end if(F1 && F2)
712
713   // check case of quadratic faces
714   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
715       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
716     return false;
717   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
718       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
719     return false;
720
721   //       5
722   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
723   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
724   //    |   / |
725   //  7 +  +  + 6
726   //    | /9  |
727   //    |/    |
728   //  4 +--+--+ 3
729   //       8
730
731   vector< const SMDS_MeshNode* > N1;
732   vector< const SMDS_MeshNode* > N2;
733   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
734     return false;
735   // now we receive following N1 and N2 (using numeration as above image)
736   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
737   // i.e. first nodes from both arrays determ new diagonal
738
739   vector< const SMDS_MeshNode*> N1new( N1.size() );
740   vector< const SMDS_MeshNode*> N2new( N2.size() );
741   N1new.back() = N1.back(); // central node of biquadratic
742   N2new.back() = N2.back();
743   N1new[0] = N1[0];  N2new[0] = N1[0];
744   N1new[1] = N2[0];  N2new[1] = N1[1];
745   N1new[2] = N2[1];  N2new[2] = N2[0];
746   N1new[3] = N1[4];  N2new[3] = N1[3];
747   N1new[4] = N2[3];  N2new[4] = N2[5];
748   N1new[5] = N1[5];  N2new[5] = N1[4];
749   // change nodes in faces
750   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
751   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
752
753   // move the central node of biquadratic triangle
754   SMESH_MesherHelper helper( *GetMesh() );
755   for ( int is2nd = 0; is2nd < 2; ++is2nd )
756   {
757     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
758     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
759     if ( nodes.size() < 7 )
760       continue;
761     helper.SetSubShape( tria->getshapeId() );
762     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
763     gp_Pnt xyz;
764     if ( F.IsNull() )
765     {
766       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
767               SMESH_TNodeXYZ( nodes[4] ) +
768               SMESH_TNodeXYZ( nodes[5] )) / 3.;
769     }
770     else
771     {
772       bool checkUV;
773       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
774                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
775                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
776       TopLoc_Location loc;
777       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
778       xyz = S->Value( uv.X(), uv.Y() );
779       xyz.Transform( loc );
780       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
781            nodes[6]->getshapeId() > 0 )
782         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
783     }
784     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
785   }
786   return true;
787 }
788
789 //=======================================================================
790 //function : findTriangles
791 //purpose  : find triangles sharing theNode1-theNode2 link
792 //=======================================================================
793
794 static bool findTriangles(const SMDS_MeshNode *    theNode1,
795                           const SMDS_MeshNode *    theNode2,
796                           const SMDS_MeshElement*& theTria1,
797                           const SMDS_MeshElement*& theTria2)
798 {
799   if ( !theNode1 || !theNode2 ) return false;
800
801   theTria1 = theTria2 = 0;
802
803   set< const SMDS_MeshElement* > emap;
804   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
805   while (it->more()) {
806     const SMDS_MeshElement* elem = it->next();
807     if ( elem->NbCornerNodes() == 3 )
808       emap.insert( elem );
809   }
810   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
811   while (it->more()) {
812     const SMDS_MeshElement* elem = it->next();
813     if ( emap.count( elem )) {
814       if ( !theTria1 )
815       {
816         theTria1 = elem;
817       }
818       else  
819       {
820         theTria2 = elem;
821         // theTria1 must be element with minimum ID
822         if ( theTria2->GetID() < theTria1->GetID() )
823           std::swap( theTria2, theTria1 );
824         return true;
825       }
826     }
827   }
828   return false;
829 }
830
831 //=======================================================================
832 //function : InverseDiag
833 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
834 //           with ones built on the same 4 nodes but having other common link.
835 //           Return false if proper faces not found
836 //=======================================================================
837
838 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
839                                     const SMDS_MeshNode * theNode2)
840 {
841   myLastCreatedElems.Clear();
842   myLastCreatedNodes.Clear();
843
844   MESSAGE( "::InverseDiag()" );
845
846   const SMDS_MeshElement *tr1, *tr2;
847   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
848     return false;
849
850   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
851   if (!F1) return false;
852   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
853   if (!F2) return false;
854   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
855       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
856
857     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
858     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
859     //    |/ |                                    | \|
860     //  B +--+ 2                                B +--+ 2
861
862     // put nodes in array
863     // and find indices of 1,2 and of A in tr1 and of B in tr2
864     int i, iA1 = 0, i1 = 0;
865     const SMDS_MeshNode* aNodes1 [3];
866     SMDS_ElemIteratorPtr it;
867     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
868       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
869       if ( aNodes1[ i ] == theNode1 )
870         iA1 = i; // node A in tr1
871       else if ( aNodes1[ i ] != theNode2 )
872         i1 = i;  // node 1
873     }
874     int iB2 = 0, i2 = 0;
875     const SMDS_MeshNode* aNodes2 [3];
876     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
877       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
878       if ( aNodes2[ i ] == theNode2 )
879         iB2 = i; // node B in tr2
880       else if ( aNodes2[ i ] != theNode1 )
881         i2 = i;  // node 2
882     }
883
884     // nodes 1 and 2 should not be the same
885     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
886       return false;
887
888     // tr1: A->2
889     aNodes1[ iA1 ] = aNodes2[ i2 ];
890     // tr2: B->1
891     aNodes2[ iB2 ] = aNodes1[ i1 ];
892
893     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
894     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
895
896     return true;
897   }
898
899   // check case of quadratic faces
900   return InverseDiag(tr1,tr2);
901 }
902
903 //=======================================================================
904 //function : getQuadrangleNodes
905 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
906 //           fusion of triangles tr1 and tr2 having shared link on
907 //           theNode1 and theNode2
908 //=======================================================================
909
910 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
911                         const SMDS_MeshNode *    theNode1,
912                         const SMDS_MeshNode *    theNode2,
913                         const SMDS_MeshElement * tr1,
914                         const SMDS_MeshElement * tr2 )
915 {
916   if( tr1->NbNodes() != tr2->NbNodes() )
917     return false;
918   // find the 4-th node to insert into tr1
919   const SMDS_MeshNode* n4 = 0;
920   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
921   int i=0;
922   while ( !n4 && i<3 ) {
923     const SMDS_MeshNode * n = cast2Node( it->next() );
924     i++;
925     bool isDiag = ( n == theNode1 || n == theNode2 );
926     if ( !isDiag )
927       n4 = n;
928   }
929   // Make an array of nodes to be in a quadrangle
930   int iNode = 0, iFirstDiag = -1;
931   it = tr1->nodesIterator();
932   i=0;
933   while ( i<3 ) {
934     const SMDS_MeshNode * n = cast2Node( it->next() );
935     i++;
936     bool isDiag = ( n == theNode1 || n == theNode2 );
937     if ( isDiag ) {
938       if ( iFirstDiag < 0 )
939         iFirstDiag = iNode;
940       else if ( iNode - iFirstDiag == 1 )
941         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
942     }
943     else if ( n == n4 ) {
944       return false; // tr1 and tr2 should not have all the same nodes
945     }
946     theQuadNodes[ iNode++ ] = n;
947   }
948   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
949     theQuadNodes[ iNode ] = n4;
950
951   return true;
952 }
953
954 //=======================================================================
955 //function : DeleteDiag
956 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
957 //           with a quadrangle built on the same 4 nodes.
958 //           Return false if proper faces not found
959 //=======================================================================
960
961 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
962                                    const SMDS_MeshNode * theNode2)
963 {
964   myLastCreatedElems.Clear();
965   myLastCreatedNodes.Clear();
966
967   MESSAGE( "::DeleteDiag()" );
968
969   const SMDS_MeshElement *tr1, *tr2;
970   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
971     return false;
972
973   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
974   if (!F1) return false;
975   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
976   if (!F2) return false;
977   SMESHDS_Mesh * aMesh = GetMeshDS();
978
979   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
980       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
981
982     const SMDS_MeshNode* aNodes [ 4 ];
983     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
984       return false;
985
986     const SMDS_MeshElement* newElem = 0;
987     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
988     myLastCreatedElems.Append(newElem);
989     AddToSameGroups( newElem, tr1, aMesh );
990     int aShapeId = tr1->getshapeId();
991     if ( aShapeId )
992       {
993         aMesh->SetMeshElementOnShape( newElem, aShapeId );
994       }
995     aMesh->RemoveElement( tr1 );
996     aMesh->RemoveElement( tr2 );
997
998     return true;
999   }
1000
1001   // check case of quadratic faces
1002   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1003     return false;
1004   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1005     return false;
1006
1007   //       5
1008   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1009   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1010   //    |   / |
1011   //  7 +  +  + 6
1012   //    | /9  |
1013   //    |/    |
1014   //  4 +--+--+ 3
1015   //       8
1016
1017   vector< const SMDS_MeshNode* > N1;
1018   vector< const SMDS_MeshNode* > N2;
1019   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1020     return false;
1021   // now we receive following N1 and N2 (using numeration as above image)
1022   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1023   // i.e. first nodes from both arrays determ new diagonal
1024
1025   const SMDS_MeshNode* aNodes[8];
1026   aNodes[0] = N1[0];
1027   aNodes[1] = N1[1];
1028   aNodes[2] = N2[0];
1029   aNodes[3] = N2[1];
1030   aNodes[4] = N1[3];
1031   aNodes[5] = N2[5];
1032   aNodes[6] = N2[3];
1033   aNodes[7] = N1[5];
1034
1035   const SMDS_MeshElement* newElem = 0;
1036   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1037                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1038   myLastCreatedElems.Append(newElem);
1039   AddToSameGroups( newElem, tr1, aMesh );
1040   int aShapeId = tr1->getshapeId();
1041   if ( aShapeId )
1042     {
1043       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044     }
1045   aMesh->RemoveElement( tr1 );
1046   aMesh->RemoveElement( tr2 );
1047
1048   // remove middle node (9)
1049   GetMeshDS()->RemoveNode( N1[4] );
1050
1051   return true;
1052 }
1053
1054 //=======================================================================
1055 //function : Reorient
1056 //purpose  : Reverse theElement orientation
1057 //=======================================================================
1058
1059 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1060 {
1061   MESSAGE("Reorient");
1062   myLastCreatedElems.Clear();
1063   myLastCreatedNodes.Clear();
1064
1065   if (!theElem)
1066     return false;
1067   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1068   if ( !it || !it->more() )
1069     return false;
1070
1071   const SMDSAbs_ElementType type = theElem->GetType();
1072   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1073     return false;
1074
1075   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1076   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1077   {
1078     const SMDS_VtkVolume* aPolyedre =
1079       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1080     if (!aPolyedre) {
1081       MESSAGE("Warning: bad volumic element");
1082       return false;
1083     }
1084     const int nbFaces = aPolyedre->NbFaces();
1085     vector<const SMDS_MeshNode *> poly_nodes;
1086     vector<int> quantities (nbFaces);
1087
1088     // reverse each face of the polyedre
1089     for (int iface = 1; iface <= nbFaces; iface++) {
1090       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1091       quantities[iface - 1] = nbFaceNodes;
1092
1093       for (inode = nbFaceNodes; inode >= 1; inode--) {
1094         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1095         poly_nodes.push_back(curNode);
1096       }
1097     }
1098     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1099   }
1100   else // other elements
1101   {
1102     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1103     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1104     if ( interlace.empty() )
1105     {
1106       std::reverse( nodes.begin(), nodes.end() ); // polygon
1107     }
1108     else if ( interlace.size() > 1 )
1109     {
1110       SMDS_MeshCell::applyInterlace( interlace, nodes );
1111     }
1112     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1113   }
1114   return false;
1115 }
1116
1117 //================================================================================
1118 /*!
1119  * \brief Reorient faces.
1120  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1121  * \param theDirection - desired direction of normal of \a theFace
1122  * \param theFace - one of \a theFaces that sould be oriented according to
1123  *        \a theDirection and whose orientation defines orientation of other faces
1124  * \return number of reoriented faces.
1125  */
1126 //================================================================================
1127
1128 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1129                                   const gp_Dir&            theDirection,
1130                                   const SMDS_MeshElement * theFace)
1131 {
1132   int nbReori = 0;
1133   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1134
1135   if ( theFaces.empty() )
1136   {
1137     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1138     while ( fIt->more() )
1139       theFaces.insert( theFaces.end(), fIt->next() );
1140   }
1141
1142   // orient theFace according to theDirection
1143   gp_XYZ normal;
1144   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1145   if ( normal * theDirection.XYZ() < 0 )
1146     nbReori += Reorient( theFace );
1147
1148   // Orient other faces
1149
1150   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1151   TIDSortedElemSet avoidSet;
1152   set< SMESH_TLink > checkedLinks;
1153   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1154
1155   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1156     theFaces.erase( theFace );
1157   startFaces.insert( theFace );
1158
1159   int nodeInd1, nodeInd2;
1160   const SMDS_MeshElement*           otherFace;
1161   vector< const SMDS_MeshElement* > facesNearLink;
1162   vector< std::pair< int, int > >   nodeIndsOfFace;
1163
1164   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1165   while ( !startFaces.empty() )
1166   {
1167     startFace = startFaces.begin();
1168     theFace = *startFace;
1169     startFaces.erase( startFace );
1170     if ( !visitedFaces.insert( theFace ).second )
1171       continue;
1172
1173     avoidSet.clear();
1174     avoidSet.insert(theFace);
1175
1176     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1177
1178     const int nbNodes = theFace->NbCornerNodes();
1179     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1180     {
1181       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1182       linkIt_isNew = checkedLinks.insert( link );
1183       if ( !linkIt_isNew.second )
1184       {
1185         // link has already been checked and won't be encountered more
1186         // if the group (theFaces) is manifold
1187         //checkedLinks.erase( linkIt_isNew.first );
1188       }
1189       else
1190       {
1191         facesNearLink.clear();
1192         nodeIndsOfFace.clear();
1193         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1194                                                              theFaces, avoidSet,
1195                                                              &nodeInd1, &nodeInd2 )))
1196           if ( otherFace != theFace)
1197           {
1198             facesNearLink.push_back( otherFace );
1199             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1200             avoidSet.insert( otherFace );
1201           }
1202         if ( facesNearLink.size() > 1 )
1203         {
1204           // NON-MANIFOLD mesh shell !
1205           // select a face most co-directed with theFace,
1206           // other faces won't be visited this time
1207           gp_XYZ NF, NOF;
1208           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1209           double proj, maxProj = -1;
1210           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1211             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1212             if (( proj = Abs( NF * NOF )) > maxProj ) {
1213               maxProj = proj;
1214               otherFace = facesNearLink[i];
1215               nodeInd1  = nodeIndsOfFace[i].first;
1216               nodeInd2  = nodeIndsOfFace[i].second;
1217             }
1218           }
1219           // not to visit rejected faces
1220           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1221             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1222               visitedFaces.insert( facesNearLink[i] );
1223         }
1224         else if ( facesNearLink.size() == 1 )
1225         {
1226           otherFace = facesNearLink[0];
1227           nodeInd1  = nodeIndsOfFace.back().first;
1228           nodeInd2  = nodeIndsOfFace.back().second;
1229         }
1230         if ( otherFace && otherFace != theFace)
1231         {
1232           // link must be reverse in otherFace if orientation ot otherFace
1233           // is same as that of theFace
1234           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1235           {
1236             nbReori += Reorient( otherFace );
1237           }
1238           startFaces.insert( otherFace );
1239         }
1240       }
1241       std::swap( link.first, link.second ); // reverse the link
1242     }
1243   }
1244   return nbReori;
1245 }
1246
1247 //================================================================================
1248 /*!
1249  * \brief Reorient faces basing on orientation of adjacent volumes.
1250  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1251  * \param theVolumes - reference volumes.
1252  * \param theOutsideNormal - to orient faces to have their normal
1253  *        pointing either \a outside or \a inside the adjacent volumes.
1254  * \return number of reoriented faces.
1255  */
1256 //================================================================================
1257
1258 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1259                                       TIDSortedElemSet & theVolumes,
1260                                       const bool         theOutsideNormal)
1261 {
1262   int nbReori = 0;
1263
1264   SMDS_ElemIteratorPtr faceIt;
1265   if ( theFaces.empty() )
1266     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1267   else
1268     faceIt = elemSetIterator( theFaces );
1269
1270   vector< const SMDS_MeshNode* > faceNodes;
1271   TIDSortedElemSet checkedVolumes;
1272   set< const SMDS_MeshNode* > faceNodesSet;
1273   SMDS_VolumeTool volumeTool;
1274
1275   while ( faceIt->more() ) // loop on given faces
1276   {
1277     const SMDS_MeshElement* face = faceIt->next();
1278     if ( face->GetType() != SMDSAbs_Face )
1279       continue;
1280
1281     const int nbCornersNodes = face->NbCornerNodes();
1282     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1283
1284     checkedVolumes.clear();
1285     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1286     while ( vIt->more() )
1287     {
1288       const SMDS_MeshElement* volume = vIt->next();
1289
1290       if ( !checkedVolumes.insert( volume ).second )
1291         continue;
1292       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1293         continue;
1294
1295       // is volume adjacent?
1296       bool allNodesCommon = true;
1297       for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1298         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1299       if ( !allNodesCommon )
1300         continue;
1301
1302       // get nodes of a corresponding volume facet
1303       faceNodesSet.clear();
1304       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1305       volumeTool.Set( volume );
1306       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1307       if ( facetID < 0 ) continue;
1308       volumeTool.SetExternalNormal();
1309       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1310
1311       // compare order of faceNodes and facetNodes
1312       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1313       int iNN[2];
1314       for ( int i = 0; i < 2; ++i )
1315       {
1316         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1317         for ( int iN = 0; iN < nbCornersNodes; ++iN )
1318           if ( faceNodes[ iN ] == n )
1319           {
1320             iNN[ i ] = iN;
1321             break;
1322           }
1323       }
1324       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1325       if ( isOutside != theOutsideNormal )
1326         nbReori += Reorient( face );
1327     }
1328   }  // loop on given faces
1329
1330   return nbReori;
1331 }
1332
1333 //=======================================================================
1334 //function : getBadRate
1335 //purpose  :
1336 //=======================================================================
1337
1338 static double getBadRate (const SMDS_MeshElement*               theElem,
1339                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1340 {
1341   SMESH::Controls::TSequenceOfXYZ P;
1342   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1343     return 1e100;
1344   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1345   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1346 }
1347
1348 //=======================================================================
1349 //function : QuadToTri
1350 //purpose  : Cut quadrangles into triangles.
1351 //           theCrit is used to select a diagonal to cut
1352 //=======================================================================
1353
1354 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1355                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1356 {
1357   myLastCreatedElems.Clear();
1358   myLastCreatedNodes.Clear();
1359
1360   if ( !theCrit.get() )
1361     return false;
1362
1363   SMESHDS_Mesh * aMesh = GetMeshDS();
1364
1365   Handle(Geom_Surface) surface;
1366   SMESH_MesherHelper   helper( *GetMesh() );
1367
1368   TIDSortedElemSet::iterator itElem;
1369   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1370   {
1371     const SMDS_MeshElement* elem = *itElem;
1372     if ( !elem || elem->GetType() != SMDSAbs_Face )
1373       continue;
1374     if ( elem->NbCornerNodes() != 4 )
1375       continue;
1376
1377     // retrieve element nodes
1378     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1379
1380     // compare two sets of possible triangles
1381     double aBadRate1, aBadRate2; // to what extent a set is bad
1382     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1383     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1384     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1385
1386     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1387     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1388     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1389
1390     const int aShapeId = FindShape( elem );
1391     const SMDS_MeshElement* newElem1 = 0;
1392     const SMDS_MeshElement* newElem2 = 0;
1393
1394     if ( !elem->IsQuadratic() ) // split liner quadrangle
1395     {
1396       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1397       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1398       if ( aBadRate1 <= aBadRate2 ) {
1399         // tr1 + tr2 is better
1400         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1401         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1402       }
1403       else {
1404         // tr3 + tr4 is better
1405         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1406         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1407       }
1408     }
1409     else // split quadratic quadrangle
1410     {
1411       helper.SetIsQuadratic( true );
1412       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1413
1414       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1415       if ( aNodes.size() == 9 )
1416       {
1417         helper.SetIsBiQuadratic( true );
1418         if ( aBadRate1 <= aBadRate2 )
1419           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1420         else
1421           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1422       }
1423       // create a new element
1424       if ( aBadRate1 <= aBadRate2 ) {
1425         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1426         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1427       }
1428       else {
1429         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1430         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1431       }
1432     } // quadratic case
1433
1434     // care of a new element
1435
1436     myLastCreatedElems.Append(newElem1);
1437     myLastCreatedElems.Append(newElem2);
1438     AddToSameGroups( newElem1, elem, aMesh );
1439     AddToSameGroups( newElem2, elem, aMesh );
1440
1441     // put a new triangle on the same shape
1442     if ( aShapeId )
1443       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1444     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1445
1446     aMesh->RemoveElement( elem );
1447   }
1448   return true;
1449 }
1450
1451 //=======================================================================
1452 /*!
1453  * \brief Split each of given quadrangles into 4 triangles.
1454  * \param theElems - The faces to be splitted. If empty all faces are split.
1455  */
1456 //=======================================================================
1457
1458 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1459 {
1460   myLastCreatedElems.Clear();
1461   myLastCreatedNodes.Clear();
1462
1463   SMESH_MesherHelper helper( *GetMesh() );
1464   helper.SetElementsOnShape( true );
1465
1466   SMDS_ElemIteratorPtr faceIt;
1467   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1468   else                    faceIt = elemSetIterator( theElems );
1469
1470   bool   checkUV;
1471   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1472   gp_XYZ xyz[9];
1473   vector< const SMDS_MeshNode* > nodes;
1474   SMESHDS_SubMesh*               subMeshDS;
1475   TopoDS_Face                    F;
1476   Handle(Geom_Surface)           surface;
1477   TopLoc_Location                loc;
1478
1479   while ( faceIt->more() )
1480   {
1481     const SMDS_MeshElement* quad = faceIt->next();
1482     if ( !quad || quad->NbCornerNodes() != 4 )
1483       continue;
1484
1485     // get a surface the quad is on
1486
1487     if ( quad->getshapeId() < 1 )
1488     {
1489       F.Nullify();
1490       helper.SetSubShape( 0 );
1491       subMeshDS = 0;
1492     }
1493     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1494     {
1495       helper.SetSubShape( quad->getshapeId() );
1496       if ( !helper.GetSubShape().IsNull() &&
1497            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1498       {
1499         F = TopoDS::Face( helper.GetSubShape() );
1500         surface = BRep_Tool::Surface( F, loc );
1501         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1502       }
1503       else
1504       {
1505         helper.SetSubShape( 0 );
1506         subMeshDS = 0;
1507       }
1508     }
1509
1510     // create a central node
1511
1512     const SMDS_MeshNode* nCentral;
1513     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1514
1515     if ( nodes.size() == 9 )
1516     {
1517       nCentral = nodes.back();
1518     }
1519     else
1520     {
1521       size_t iN = 0;
1522       if ( F.IsNull() )
1523       {
1524         for ( ; iN < nodes.size(); ++iN )
1525           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1526
1527         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1528           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1529
1530         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1531                                    xyz[0], xyz[1], xyz[2], xyz[3],
1532                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1533       }
1534       else
1535       {
1536         for ( ; iN < nodes.size(); ++iN )
1537           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1538
1539         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1540           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1541
1542         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543                                   uv[0], uv[1], uv[2], uv[3],
1544                                   uv[4], uv[5], uv[6], uv[7] );
1545
1546         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1547         xyz[ 8 ] = p.XYZ();
1548       }
1549
1550       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1551                                  uv[8].X(), uv[8].Y() );
1552       myLastCreatedNodes.Append( nCentral );
1553     }
1554
1555     // create 4 triangles
1556
1557     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1558     
1559     helper.SetIsQuadratic  ( nodes.size() > 4 );
1560     helper.SetIsBiQuadratic( nodes.size() == 9 );
1561     if ( helper.GetIsQuadratic() )
1562       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1563
1564     for ( int i = 0; i < 4; ++i )
1565     {
1566       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1567                                                nodes[(i+1)%4],
1568                                                nCentral );
1569       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1570       myLastCreatedElems.Append( tria );
1571     }
1572   }
1573 }
1574
1575 //=======================================================================
1576 //function : BestSplit
1577 //purpose  : Find better diagonal for cutting.
1578 //=======================================================================
1579
1580 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1581                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1582 {
1583   myLastCreatedElems.Clear();
1584   myLastCreatedNodes.Clear();
1585
1586   if (!theCrit.get())
1587     return -1;
1588
1589   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1590     return -1;
1591
1592   if( theQuad->NbNodes()==4 ||
1593       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1594
1595     // retrieve element nodes
1596     const SMDS_MeshNode* aNodes [4];
1597     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1598     int i = 0;
1599     //while (itN->more())
1600     while (i<4) {
1601       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1602     }
1603     // compare two sets of possible triangles
1604     double aBadRate1, aBadRate2; // to what extent a set is bad
1605     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1606     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1607     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1608
1609     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1610     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1611     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1612     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1613     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1614     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1615       return 1; // diagonal 1-3
1616
1617     return 2; // diagonal 2-4
1618   }
1619   return -1;
1620 }
1621
1622 namespace
1623 {
1624   // Methods of splitting volumes into tetra
1625
1626   const int theHexTo5_1[5*4+1] =
1627     {
1628       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1629     };
1630   const int theHexTo5_2[5*4+1] =
1631     {
1632       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1633     };
1634   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1635
1636   const int theHexTo6_1[6*4+1] =
1637     {
1638       1, 5, 6, 0,    0, 1, 2, 6,     0, 4, 5, 6,    0, 4, 6, 7,     0, 2, 3, 6,   0, 3, 7, 6,  -1
1639     };
1640   const int theHexTo6_2[6*4+1] =
1641     {
1642       2, 6, 7, 1,    1, 2, 3, 7,     1, 5, 6, 7,    1, 5, 7, 4,     1, 3, 0, 7,   1, 0, 4, 7,  -1
1643     };
1644   const int theHexTo6_3[6*4+1] =
1645     {
1646       3, 7, 4, 2,    2, 3, 0, 4,     2, 6, 7, 4,    2, 6, 4, 5,     2, 0, 1, 4,   2, 1, 5, 4,  -1
1647     };
1648   const int theHexTo6_4[6*4+1] =
1649     {
1650       0, 4, 5, 3,    3, 0, 1, 5,     3, 7, 4, 5,    3, 7, 5, 6,     3, 1, 2, 5,   3, 2, 6, 5,  -1
1651     };
1652   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1653
1654   const int thePyraTo2_1[2*4+1] =
1655     {
1656       0, 1, 2, 4,    0, 2, 3, 4,   -1
1657     };
1658   const int thePyraTo2_2[2*4+1] =
1659     {
1660       1, 2, 3, 4,    1, 3, 0, 4,   -1
1661     };
1662   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1663
1664   const int thePentaTo3_1[3*4+1] =
1665     {
1666       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1667     };
1668   const int thePentaTo3_2[3*4+1] =
1669     {
1670       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1671     };
1672   const int thePentaTo3_3[3*4+1] =
1673     {
1674       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1675     };
1676   const int thePentaTo3_4[3*4+1] =
1677     {
1678       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1679     };
1680   const int thePentaTo3_5[3*4+1] =
1681     {
1682       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1683     };
1684   const int thePentaTo3_6[3*4+1] =
1685     {
1686       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1687     };
1688   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1689                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1690
1691   // Methods of splitting hexahedron into prisms
1692
1693   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1694     {
1695       0, 1, 8, 4, 5, 9,    1, 2, 8, 5, 6, 9,    2, 3, 8, 6, 7, 9,   3, 0, 8, 7, 4, 9,    -1
1696     };
1697   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1698     {
1699       1, 0, 8, 2, 3, 9,    0, 4, 8, 3, 7, 9,    4, 5, 8, 7, 6, 9,   5, 1, 8, 6, 2, 9,    -1
1700     };
1701   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1702     {
1703       0, 3, 9, 1, 2, 8,    3, 7, 9, 2, 6, 8,    7, 4, 9, 6, 5, 8,   4, 0, 9, 5, 1, 8,    -1
1704     };
1705
1706   const int theHexTo2Prisms_BT_1[6*2+1] =
1707     {
1708       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1709     };
1710   const int theHexTo2Prisms_BT_2[6*2+1] =
1711     {
1712       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1713     };
1714   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1715
1716   const int theHexTo2Prisms_LR_1[6*2+1] =
1717     {
1718       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1719     };
1720   const int theHexTo2Prisms_LR_2[6*2+1] =
1721     {
1722       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1723     };
1724   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1725
1726   const int theHexTo2Prisms_FB_1[6*2+1] =
1727     {
1728       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1729     };
1730   const int theHexTo2Prisms_FB_2[6*2+1] =
1731     {
1732       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1733     };
1734   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1735
1736
1737   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1738   {
1739     int _n1, _n2, _n3;
1740     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1741     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1742     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1743                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1744   };
1745   struct TSplitMethod
1746   {
1747     int        _nbSplits;
1748     int        _nbCorners;
1749     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1750     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1751     bool       _ownConn;      //!< to delete _connectivity in destructor
1752     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1753
1754     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1755       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1756     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1757     bool hasFacet( const TTriangleFacet& facet ) const
1758     {
1759       if ( _nbCorners == 4 )
1760       {
1761         const int* tetConn = _connectivity;
1762         for ( ; tetConn[0] >= 0; tetConn += 4 )
1763           if (( facet.contains( tetConn[0] ) +
1764                 facet.contains( tetConn[1] ) +
1765                 facet.contains( tetConn[2] ) +
1766                 facet.contains( tetConn[3] )) == 3 )
1767             return true;
1768       }
1769       else // prism, _nbCorners == 6
1770       {
1771         const int* prismConn = _connectivity;
1772         for ( ; prismConn[0] >= 0; prismConn += 6 )
1773         {
1774           if (( facet.contains( prismConn[0] ) &&
1775                 facet.contains( prismConn[1] ) &&
1776                 facet.contains( prismConn[2] ))
1777               ||
1778               ( facet.contains( prismConn[3] ) &&
1779                 facet.contains( prismConn[4] ) &&
1780                 facet.contains( prismConn[5] )))
1781             return true;
1782         }
1783       }
1784       return false;
1785     }
1786   };
1787
1788   //=======================================================================
1789   /*!
1790    * \brief return TSplitMethod for the given element to split into tetrahedra
1791    */
1792   //=======================================================================
1793
1794   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1795   {
1796     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1797
1798     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1799     // an edge and a face barycenter; tertaherdons are based on triangles and
1800     // a volume barycenter
1801     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1802
1803     // Find out how adjacent volumes are split
1804
1805     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1806     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1807     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1808     {
1809       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1810       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1811       if ( nbNodes < 4 ) continue;
1812
1813       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1814       const int* nInd = vol.GetFaceNodesIndices( iF );
1815       if ( nbNodes == 4 )
1816       {
1817         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1818         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1819         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1820         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1821       }
1822       else
1823       {
1824         int iCom = 0; // common node of triangle faces to split into
1825         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1826         {
1827           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1828                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1829                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1830           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1831                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1832                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1833           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1834           {
1835             triaSplits.push_back( t012 );
1836             triaSplits.push_back( t023 );
1837             break;
1838           }
1839         }
1840       }
1841       if ( !triaSplits.empty() )
1842         hasAdjacentSplits = true;
1843     }
1844
1845     // Among variants of split method select one compliant with adjacent volumes
1846
1847     TSplitMethod method;
1848     if ( !vol.Element()->IsPoly() && !is24TetMode )
1849     {
1850       int nbVariants = 2, nbTet = 0;
1851       const int** connVariants = 0;
1852       switch ( vol.Element()->GetEntityType() )
1853       {
1854       case SMDSEntity_Hexa:
1855       case SMDSEntity_Quad_Hexa:
1856       case SMDSEntity_TriQuad_Hexa:
1857         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1858           connVariants = theHexTo5, nbTet = 5;
1859         else
1860           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1861         break;
1862       case SMDSEntity_Pyramid:
1863       case SMDSEntity_Quad_Pyramid:
1864         connVariants = thePyraTo2;  nbTet = 2;
1865         break;
1866       case SMDSEntity_Penta:
1867       case SMDSEntity_Quad_Penta:
1868         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1869         break;
1870       default:
1871         nbVariants = 0;
1872       }
1873       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1874       {
1875         // check method compliancy with adjacent tetras,
1876         // all found splits must be among facets of tetras described by this method
1877         method = TSplitMethod( nbTet, connVariants[variant] );
1878         if ( hasAdjacentSplits && method._nbSplits > 0 )
1879         {
1880           bool facetCreated = true;
1881           for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1882           {
1883             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1884             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1885               facetCreated = method.hasFacet( *facet );
1886           }
1887           if ( !facetCreated )
1888             method = TSplitMethod(0); // incompatible method
1889         }
1890       }
1891     }
1892     if ( method._nbSplits < 1 )
1893     {
1894       // No standard method is applicable, use a generic solution:
1895       // each facet of a volume is split into triangles and
1896       // each of triangles and a volume barycenter form a tetrahedron.
1897
1898       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1899
1900       int* connectivity = new int[ maxTetConnSize + 1 ];
1901       method._connectivity = connectivity;
1902       method._ownConn = true;
1903       method._baryNode = !isHex27; // to create central node or not
1904
1905       int connSize = 0;
1906       int baryCenInd = vol.NbNodes() - int( isHex27 );
1907       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1908       {
1909         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1910         const int*   nInd = vol.GetFaceNodesIndices( iF );
1911         // find common node of triangle facets of tetra to create
1912         int iCommon = 0; // index in linear numeration
1913         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1914         if ( !triaSplits.empty() )
1915         {
1916           // by found facets
1917           const TTriangleFacet* facet = &triaSplits.front();
1918           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1919             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1920                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1921               break;
1922         }
1923         else if ( nbNodes > 3 && !is24TetMode )
1924         {
1925           // find the best method of splitting into triangles by aspect ratio
1926           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1927           map< double, int > badness2iCommon;
1928           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1929           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1930           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1931           {
1932             double badness = 0;
1933             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1934             {
1935               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1936                                       nodes[ iQ*((iLast-1)%nbNodes)],
1937                                       nodes[ iQ*((iLast  )%nbNodes)]);
1938               badness += getBadRate( &tria, aspectRatio );
1939             }
1940             badness2iCommon.insert( make_pair( badness, iCommon ));
1941           }
1942           // use iCommon with lowest badness
1943           iCommon = badness2iCommon.begin()->second;
1944         }
1945         if ( iCommon >= nbNodes )
1946           iCommon = 0; // something wrong
1947
1948         // fill connectivity of tetrahedra based on a current face
1949         int nbTet = nbNodes - 2;
1950         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1951         {
1952           int faceBaryCenInd;
1953           if ( isHex27 )
1954           {
1955             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1956             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1957           }
1958           else
1959           {
1960             method._faceBaryNode[ iF ] = 0;
1961             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1962           }
1963           nbTet = nbNodes;
1964           for ( int i = 0; i < nbTet; ++i )
1965           {
1966             int i1 = i, i2 = (i+1) % nbNodes;
1967             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1968             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1969             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1970             connectivity[ connSize++ ] = faceBaryCenInd;
1971             connectivity[ connSize++ ] = baryCenInd;
1972           }
1973         }
1974         else
1975         {
1976           for ( int i = 0; i < nbTet; ++i )
1977           {
1978             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1979             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1981             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1982             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1983             connectivity[ connSize++ ] = baryCenInd;
1984           }
1985         }
1986         method._nbSplits += nbTet;
1987
1988       } // loop on volume faces
1989
1990       connectivity[ connSize++ ] = -1;
1991
1992     } // end of generic solution
1993
1994     return method;
1995   }
1996   //=======================================================================
1997   /*!
1998    * \brief return TSplitMethod to split haxhedron into prisms
1999    */
2000   //=======================================================================
2001
2002   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2003                                     const int        methodFlags,
2004                                     const int        facetToSplit)
2005   {
2006     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2007     // B, T, L, B, R, F
2008     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2009
2010     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2011     {
2012       static TSplitMethod to4methods[4]; // order BT, LR, FB
2013       if ( to4methods[iF]._nbSplits == 0 )
2014       {
2015         switch ( iF ) {
2016         case 0:
2017           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2018           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2019           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2020           break;
2021         case 1:
2022           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2023           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2024           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2025           break;
2026         case 2:
2027           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2028           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2029           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2030           break;
2031         default: return to4methods[3];
2032         }
2033         to4methods[iF]._nbSplits  = 4;
2034         to4methods[iF]._nbCorners = 6;
2035       }
2036       return to4methods[iF];
2037     }
2038     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2039
2040     TSplitMethod method;
2041
2042     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2043
2044     const int nbVariants = 2, nbSplits = 2;
2045     const int** connVariants = 0;
2046     switch ( iF ) {
2047     case 0: connVariants = theHexTo2Prisms_BT; break;
2048     case 1: connVariants = theHexTo2Prisms_LR; break;
2049     case 2: connVariants = theHexTo2Prisms_FB; break;
2050     default: return method;
2051     }
2052
2053     // look for prisms adjacent via facetToSplit and an opposite one
2054     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2055     {
2056       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2057       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2058       if ( nbNodes != 4 ) return method;
2059
2060       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2061       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2062       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2063       TTriangleFacet* t;
2064       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2065         t = &t012;
2066       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2067         t = &t123;
2068       else
2069         continue;
2070
2071       // there are adjacent prism
2072       for ( int variant = 0; variant < nbVariants; ++variant )
2073       {
2074         // check method compliancy with adjacent prisms,
2075         // the found prism facets must be among facets of prisms described by current method
2076         method._nbSplits     = nbSplits;
2077         method._nbCorners    = 6;
2078         method._connectivity = connVariants[ variant ];
2079         if ( method.hasFacet( *t ))
2080           return method;
2081       }
2082     }
2083
2084     // No adjacent prisms. Select a variant with a best aspect ratio.
2085
2086     double badness[2] = { 0, 0 };
2087     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2088     const SMDS_MeshNode** nodes = vol.GetNodes();
2089     for ( int variant = 0; variant < nbVariants; ++variant )
2090       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2091       {
2092         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2093         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2094
2095         method._connectivity = connVariants[ variant ];
2096         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2097         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2098         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2099
2100         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2101                                 nodes[ t->_n2 ],
2102                                 nodes[ t->_n3 ] );
2103         badness[ variant ] += getBadRate( &tria, aspectRatio );
2104       }
2105     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2106
2107     method._nbSplits     = nbSplits;
2108     method._nbCorners    = 6;
2109     method._connectivity = connVariants[ iBetter ];
2110
2111     return method;
2112   }
2113
2114   //================================================================================
2115   /*!
2116    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2117    */
2118   //================================================================================
2119
2120   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2121                                        const SMDSAbs_GeometryType geom ) const
2122   {
2123     // find the tetrahedron including the three nodes of facet
2124     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2125     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2126     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2127     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2128     while ( volIt1->more() )
2129     {
2130       const SMDS_MeshElement* v = volIt1->next();
2131       if ( v->GetGeomType() != geom )
2132         continue;
2133       const int lastCornerInd = v->NbCornerNodes() - 1;
2134       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2135         continue; // medium node not allowed
2136       const int ind2 = v->GetNodeIndex( n2 );
2137       if ( ind2 < 0 || lastCornerInd < ind2 )
2138         continue;
2139       const int ind3 = v->GetNodeIndex( n3 );
2140       if ( ind3 < 0 || lastCornerInd < ind3 )
2141         continue;
2142       return true;
2143     }
2144     return false;
2145   }
2146
2147   //=======================================================================
2148   /*!
2149    * \brief A key of a face of volume
2150    */
2151   //=======================================================================
2152
2153   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2154   {
2155     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2156     {
2157       TIDSortedNodeSet sortedNodes;
2158       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2159       int nbNodes = vol.NbFaceNodes( iF );
2160       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2161       for ( int i = 0; i < nbNodes; i += iQ )
2162         sortedNodes.insert( fNodes[i] );
2163       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2164       first.first   = (*(n++))->GetID();
2165       first.second  = (*(n++))->GetID();
2166       second.first  = (*(n++))->GetID();
2167       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2168     }
2169   };
2170 } // namespace
2171
2172 //=======================================================================
2173 //function : SplitVolumes
2174 //purpose  : Split volume elements into tetrahedra or prisms.
2175 //           If facet ID < 0, element is split into tetrahedra,
2176 //           else a hexahedron is split into prisms so that the given facet is
2177 //           split into triangles
2178 //=======================================================================
2179
2180 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2181                                      const int            theMethodFlags)
2182 {
2183   SMDS_VolumeTool    volTool;
2184   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2185   fHelper.ToFixNodeParameters( true );
2186
2187   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2188   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2189
2190   SMESH_SequenceOfElemPtr newNodes, newElems;
2191
2192   // map face of volume to it's baricenrtic node
2193   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2194   double bc[3];
2195
2196   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2197   for ( ; elem2facet != theElems.end(); ++elem2facet )
2198   {
2199     const SMDS_MeshElement* elem = elem2facet->first;
2200     const int       facetToSplit = elem2facet->second;
2201     if ( elem->GetType() != SMDSAbs_Volume )
2202       continue;
2203     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2204     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2205       continue;
2206
2207     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2208
2209     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2210                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2211                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2212     if ( splitMethod._nbSplits < 1 ) continue;
2213
2214     // find submesh to add new tetras to
2215     if ( !subMesh || !subMesh->Contains( elem ))
2216     {
2217       int shapeID = FindShape( elem );
2218       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2219       subMesh = GetMeshDS()->MeshElements( shapeID );
2220     }
2221     int iQ;
2222     if ( elem->IsQuadratic() )
2223     {
2224       iQ = 2;
2225       // add quadratic links to the helper
2226       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2227       {
2228         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2229         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2230         for ( int iN = 0; iN < nbN; iN += iQ )
2231           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2232       }
2233       helper.SetIsQuadratic( true );
2234     }
2235     else
2236     {
2237       iQ = 1;
2238       helper.SetIsQuadratic( false );
2239     }
2240     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2241                                         volTool.GetNodes() + elem->NbNodes() );
2242     helper.SetElementsOnShape( true );
2243     if ( splitMethod._baryNode )
2244     {
2245       // make a node at barycenter
2246       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2247       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2248       nodes.push_back( gcNode );
2249       newNodes.Append( gcNode );
2250     }
2251     if ( !splitMethod._faceBaryNode.empty() )
2252     {
2253       // make or find baricentric nodes of faces
2254       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2255       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2256       {
2257         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2258           volFace2BaryNode.insert
2259           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2260         if ( !f_n->second )
2261         {
2262           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2263           newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2264         }
2265         nodes.push_back( iF_n->second = f_n->second );
2266       }
2267     }
2268
2269     // make new volumes
2270     vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2271     const int* volConn = splitMethod._connectivity;
2272     if ( splitMethod._nbCorners == 4 ) // tetra
2273       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2274         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2275                                                             nodes[ volConn[1] ],
2276                                                             nodes[ volConn[2] ],
2277                                                             nodes[ volConn[3] ]));
2278     else // prisms
2279       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2280         newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2281                                                             nodes[ volConn[1] ],
2282                                                             nodes[ volConn[2] ],
2283                                                             nodes[ volConn[3] ],
2284                                                             nodes[ volConn[4] ],
2285                                                             nodes[ volConn[5] ]));
2286
2287     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2288
2289     // Split faces on sides of the split volume
2290
2291     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2292     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2293     {
2294       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2295       if ( nbNodes < 4 ) continue;
2296
2297       // find an existing face
2298       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2299                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2300       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2301                                                                        /*noMedium=*/false))
2302       {
2303         // make triangles
2304         helper.SetElementsOnShape( false );
2305         vector< const SMDS_MeshElement* > triangles;
2306
2307         // find submesh to add new triangles in
2308         if ( !fSubMesh || !fSubMesh->Contains( face ))
2309         {
2310           int shapeID = FindShape( face );
2311           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2312         }
2313         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2314         if ( iF_n != splitMethod._faceBaryNode.end() )
2315         {
2316           const SMDS_MeshNode *baryNode = iF_n->second;
2317           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2318           {
2319             const SMDS_MeshNode* n1 = fNodes[iN];
2320             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2321             const SMDS_MeshNode *n3 = baryNode;
2322             if ( !volTool.IsFaceExternal( iF ))
2323               swap( n2, n3 );
2324             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2325           }
2326           if ( fSubMesh ) // update position of the bary node on geometry
2327           {
2328             if ( subMesh )
2329               subMesh->RemoveNode( baryNode, false );
2330             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2331             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2332             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2333             {
2334               fHelper.SetSubShape( s );
2335               gp_XY uv( 1e100, 1e100 );
2336               double distXYZ[4];
2337               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2338                                         uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2339                    uv.X() < 1e100 )
2340               {
2341                 // node is too far from the surface
2342                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2343                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2344                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2345               }
2346             }
2347           }
2348         }
2349         else
2350         {
2351           // among possible triangles create ones discribed by split method
2352           const int* nInd = volTool.GetFaceNodesIndices( iF );
2353           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2354           int iCom = 0; // common node of triangle faces to split into
2355           list< TTriangleFacet > facets;
2356           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2357           {
2358             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2359                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2360                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2361             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2362                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2363                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2364             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2365             {
2366               facets.push_back( t012 );
2367               facets.push_back( t023 );
2368               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2369                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2370                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2371                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2372               break;
2373             }
2374           }
2375           list< TTriangleFacet >::iterator facet = facets.begin();
2376           if ( facet == facets.end() )
2377             break;
2378           for ( ; facet != facets.end(); ++facet )
2379           {
2380             if ( !volTool.IsFaceExternal( iF ))
2381               swap( facet->_n2, facet->_n3 );
2382             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2383                                                  volNodes[ facet->_n2 ],
2384                                                  volNodes[ facet->_n3 ]));
2385           }
2386         }
2387         for ( int i = 0; i < triangles.size(); ++i )
2388         {
2389           if ( !triangles[i] ) continue;
2390           if ( fSubMesh )
2391             fSubMesh->AddElement( triangles[i]);
2392           newElems.Append( triangles[i] );
2393         }
2394         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2395         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2396
2397       } // while a face based on facet nodes exists
2398     } // loop on volume faces to split them into triangles
2399
2400     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2401
2402     if ( geomType == SMDSEntity_TriQuad_Hexa )
2403     {
2404       // remove medium nodes that could become free
2405       for ( int i = 20; i < volTool.NbNodes(); ++i )
2406         if ( volNodes[i]->NbInverseElements() == 0 )
2407           GetMeshDS()->RemoveNode( volNodes[i] );
2408     }
2409   } // loop on volumes to split
2410   
2411   myLastCreatedNodes = newNodes;
2412   myLastCreatedElems = newElems;
2413 }
2414
2415 //=======================================================================
2416 //function : GetHexaFacetsToSplit
2417 //purpose  : For hexahedra that will be split into prisms, finds facets to
2418 //           split into triangles. Only hexahedra adjacent to the one closest
2419 //           to theFacetNormal.Location() are returned.
2420 //param [in,out] theHexas - the hexahedra
2421 //param [in]     theFacetNormal - facet normal
2422 //param [out]    theFacets - the hexahedra and found facet IDs
2423 //=======================================================================
2424
2425 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2426                                              const gp_Ax1&     theFacetNormal,
2427                                              TFacetOfElem &    theFacets)
2428 {
2429   #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2430
2431   // Find a hexa closest to the location of theFacetNormal
2432
2433   const SMDS_MeshElement* startHex;
2434   {
2435     // get SMDS_ElemIteratorPtr on theHexas
2436     typedef const SMDS_MeshElement*                                      TValue;
2437     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2438     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2439     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2440     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2441     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2442       ( new TElemSetIter( theHexas.begin(),
2443                           theHexas.end(),
2444                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2445
2446     SMESH_ElementSearcher* searcher =
2447       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2448
2449     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2450
2451     delete searcher;
2452
2453     if ( !startHex )
2454       throw SALOME_Exception( THIS_METHOD "startHex not found");
2455   }
2456
2457   // Select a facet of startHex by theFacetNormal
2458
2459   SMDS_VolumeTool vTool( startHex );
2460   double norm[3], dot, maxDot = 0;
2461   int facetID = -1;
2462   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2463     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2464     {
2465       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2466       if ( dot > maxDot )
2467       {
2468         facetID = iF;
2469         maxDot = dot;
2470       }
2471     }
2472   if ( facetID < 0 )
2473     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2474
2475   // Fill theFacets starting from facetID of startHex
2476
2477   // facets used for seach of volumes adjacent to already treated ones
2478   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2479   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2480   TFacetMap facetsToCheck;
2481
2482   set<const SMDS_MeshNode*> facetNodes;
2483   const SMDS_MeshElement*   curHex;
2484
2485   const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2486
2487   while ( startHex )
2488   {
2489     // move in two directions from startHex via facetID
2490     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2491     {
2492       curHex       = startHex;
2493       int curFacet = facetID;
2494       if ( is2nd ) // do not treat startHex twice
2495       {
2496         vTool.Set( curHex );
2497         if ( vTool.IsFreeFace( curFacet, &curHex ))
2498         {
2499           curHex = 0;
2500         }
2501         else
2502         {
2503           vTool.GetFaceNodes( curFacet, facetNodes );
2504           vTool.Set( curHex );
2505           curFacet = vTool.GetFaceIndex( facetNodes );
2506         }
2507       }
2508       while ( curHex )
2509       {
2510         // store a facet to split
2511         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2512         {
2513           theFacets.insert( make_pair( curHex, -1 ));
2514           break;
2515         }
2516         if ( !allHex && !theHexas.count( curHex ))
2517           break;
2518
2519         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2520           theFacets.insert( make_pair( curHex, curFacet ));
2521         if ( !facetIt2isNew.second )
2522           break;
2523
2524         // remember not-to-split facets in facetsToCheck
2525         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2526         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2527         {
2528           if ( iF == curFacet && iF == oppFacet )
2529             continue;
2530           TVolumeFaceKey facetKey ( vTool, iF );
2531           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2532           pair< TFacetMap::iterator, bool > it2isnew =
2533             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2534           if ( !it2isnew.second )
2535             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2536         }
2537         // pass to a volume adjacent via oppFacet
2538         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2539         {
2540           curHex = 0;
2541         }
2542         else
2543         {
2544           // get a new curFacet
2545           vTool.GetFaceNodes( oppFacet, facetNodes );
2546           vTool.Set( curHex );
2547           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2548         }
2549       }
2550     } // move in two directions from startHex via facetID
2551
2552     // Find a new startHex by facetsToCheck
2553
2554     startHex = 0;
2555     facetID  = -1;
2556     TFacetMap::iterator fIt = facetsToCheck.begin();
2557     while ( !startHex && fIt != facetsToCheck.end() )
2558     {
2559       const TElemFacets&  elemFacets = fIt->second;
2560       const SMDS_MeshElement*    hex = elemFacets.first->first;
2561       int                 splitFacet = elemFacets.first->second;
2562       int               lateralFacet = elemFacets.second;
2563       facetsToCheck.erase( fIt );
2564       fIt = facetsToCheck.begin();
2565
2566       vTool.Set( hex );
2567       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2568            curHex->GetGeomType() != SMDSGeom_HEXA )
2569         continue;
2570       if ( !allHex && !theHexas.count( curHex ))
2571         continue;
2572
2573       startHex = curHex;
2574
2575       // find a facet of startHex to split 
2576
2577       set<const SMDS_MeshNode*> lateralNodes;
2578       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2579       vTool.GetFaceNodes( splitFacet,   facetNodes );
2580       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2581       vTool.Set( startHex );
2582       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2583
2584       // look for a facet of startHex having common nodes with facetNodes
2585       // but not lateralFacet
2586       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2587       {
2588         if ( iF == lateralFacet )
2589           continue;
2590         int nbCommonNodes = 0;
2591         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2592         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2593           nbCommonNodes += facetNodes.count( nn[ iN ]);
2594
2595         if ( nbCommonNodes >= 2 )
2596         {
2597           facetID = iF;
2598           break;
2599         }
2600       }
2601       if ( facetID < 0 )
2602         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2603     }
2604   } //   while ( startHex )
2605 }
2606
2607 //=======================================================================
2608 //function : AddToSameGroups
2609 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2610 //=======================================================================
2611
2612 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2613                                         const SMDS_MeshElement* elemInGroups,
2614                                         SMESHDS_Mesh *          aMesh)
2615 {
2616   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2617   if (!groups.empty()) {
2618     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2619     for ( ; grIt != groups.end(); grIt++ ) {
2620       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2621       if ( group && group->Contains( elemInGroups ))
2622         group->SMDSGroup().Add( elemToAdd );
2623     }
2624   }
2625 }
2626
2627
2628 //=======================================================================
2629 //function : RemoveElemFromGroups
2630 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2631 //=======================================================================
2632 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2633                                              SMESHDS_Mesh *          aMesh)
2634 {
2635   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2636   if (!groups.empty())
2637   {
2638     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2639     for (; GrIt != groups.end(); GrIt++)
2640     {
2641       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2642       if (!grp || grp->IsEmpty()) continue;
2643       grp->SMDSGroup().Remove(removeelem);
2644     }
2645   }
2646 }
2647
2648 //================================================================================
2649 /*!
2650  * \brief Replace elemToRm by elemToAdd in the all groups
2651  */
2652 //================================================================================
2653
2654 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2655                                             const SMDS_MeshElement* elemToAdd,
2656                                             SMESHDS_Mesh *          aMesh)
2657 {
2658   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2659   if (!groups.empty()) {
2660     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2661     for ( ; grIt != groups.end(); grIt++ ) {
2662       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2663       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2664         group->SMDSGroup().Add( elemToAdd );
2665     }
2666   }
2667 }
2668
2669 //================================================================================
2670 /*!
2671  * \brief Replace elemToRm by elemToAdd in the all groups
2672  */
2673 //================================================================================
2674
2675 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2676                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2677                                             SMESHDS_Mesh *                         aMesh)
2678 {
2679   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2680   if (!groups.empty())
2681   {
2682     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2683     for ( ; grIt != groups.end(); grIt++ ) {
2684       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2685       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2686         for ( int i = 0; i < elemToAdd.size(); ++i )
2687           group->SMDSGroup().Add( elemToAdd[ i ] );
2688     }
2689   }
2690 }
2691
2692 //=======================================================================
2693 //function : QuadToTri
2694 //purpose  : Cut quadrangles into triangles.
2695 //           theCrit is used to select a diagonal to cut
2696 //=======================================================================
2697
2698 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2699                                   const bool         the13Diag)
2700 {
2701   myLastCreatedElems.Clear();
2702   myLastCreatedNodes.Clear();
2703
2704   MESSAGE( "::QuadToTri()" );
2705
2706   SMESHDS_Mesh * aMesh = GetMeshDS();
2707
2708   Handle(Geom_Surface) surface;
2709   SMESH_MesherHelper   helper( *GetMesh() );
2710
2711   TIDSortedElemSet::iterator itElem;
2712   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2713     const SMDS_MeshElement* elem = *itElem;
2714     if ( !elem || elem->GetType() != SMDSAbs_Face )
2715       continue;
2716     bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2717     if(!isquad) continue;
2718
2719     if(elem->NbNodes()==4) {
2720       // retrieve element nodes
2721       const SMDS_MeshNode* aNodes [4];
2722       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2723       int i = 0;
2724       while ( itN->more() )
2725         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2726
2727       int aShapeId = FindShape( elem );
2728       const SMDS_MeshElement* newElem1 = 0;
2729       const SMDS_MeshElement* newElem2 = 0;
2730       if ( the13Diag ) {
2731         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2732         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2733       }
2734       else {
2735         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2736         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2737       }
2738       myLastCreatedElems.Append(newElem1);
2739       myLastCreatedElems.Append(newElem2);
2740       // put a new triangle on the same shape and add to the same groups
2741       if ( aShapeId )
2742         {
2743           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2744           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2745         }
2746       AddToSameGroups( newElem1, elem, aMesh );
2747       AddToSameGroups( newElem2, elem, aMesh );
2748       //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2749       aMesh->RemoveElement( elem );
2750     }
2751
2752     // Quadratic quadrangle
2753
2754     if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2755
2756       // get surface elem is on
2757       int aShapeId = FindShape( elem );
2758       if ( aShapeId != helper.GetSubShapeID() ) {
2759         surface.Nullify();
2760         TopoDS_Shape shape;
2761         if ( aShapeId > 0 )
2762           shape = aMesh->IndexToShape( aShapeId );
2763         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2764           TopoDS_Face face = TopoDS::Face( shape );
2765           surface = BRep_Tool::Surface( face );
2766           if ( !surface.IsNull() )
2767             helper.SetSubShape( shape );
2768         }
2769       }
2770
2771       const SMDS_MeshNode* aNodes [8];
2772       const SMDS_MeshNode* inFaceNode = 0;
2773       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2774       int i = 0;
2775       while ( itN->more() ) {
2776         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2777         if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2778              aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2779         {
2780           inFaceNode = aNodes[ i-1 ];
2781         }
2782       }
2783
2784       // find middle point for (0,1,2,3)
2785       // and create a node in this point;
2786       gp_XYZ p( 0,0,0 );
2787       if ( surface.IsNull() ) {
2788         for(i=0; i<4; i++)
2789           p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2790         p /= 4;
2791       }
2792       else {
2793         TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2794         gp_XY uv( 0,0 );
2795         for(i=0; i<4; i++)
2796           uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2797         uv /= 4.;
2798         p = surface->Value( uv.X(), uv.Y() ).XYZ();
2799       }
2800       const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2801       myLastCreatedNodes.Append(newN);
2802
2803       // create a new element
2804       const SMDS_MeshElement* newElem1 = 0;
2805       const SMDS_MeshElement* newElem2 = 0;
2806       if ( the13Diag ) {
2807         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2808                                   aNodes[6], aNodes[7], newN );
2809         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2810                                   newN,      aNodes[4], aNodes[5] );
2811       }
2812       else {
2813         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2814                                   aNodes[7], aNodes[4], newN );
2815         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2816                                   newN,      aNodes[5], aNodes[6] );
2817       }
2818       myLastCreatedElems.Append(newElem1);
2819       myLastCreatedElems.Append(newElem2);
2820       // put a new triangle on the same shape and add to the same groups
2821       if ( aShapeId )
2822         {
2823           aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2824           aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2825         }
2826       AddToSameGroups( newElem1, elem, aMesh );
2827       AddToSameGroups( newElem2, elem, aMesh );
2828       aMesh->RemoveElement( elem );
2829     }
2830   }
2831
2832   return true;
2833 }
2834
2835 //=======================================================================
2836 //function : getAngle
2837 //purpose  :
2838 //=======================================================================
2839
2840 double getAngle(const SMDS_MeshElement * tr1,
2841                 const SMDS_MeshElement * tr2,
2842                 const SMDS_MeshNode *    n1,
2843                 const SMDS_MeshNode *    n2)
2844 {
2845   double angle = 2. * M_PI; // bad angle
2846
2847   // get normals
2848   SMESH::Controls::TSequenceOfXYZ P1, P2;
2849   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2850        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2851     return angle;
2852   gp_Vec N1,N2;
2853   if(!tr1->IsQuadratic())
2854     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2855   else
2856     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2857   if ( N1.SquareMagnitude() <= gp::Resolution() )
2858     return angle;
2859   if(!tr2->IsQuadratic())
2860     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2861   else
2862     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2863   if ( N2.SquareMagnitude() <= gp::Resolution() )
2864     return angle;
2865
2866   // find the first diagonal node n1 in the triangles:
2867   // take in account a diagonal link orientation
2868   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2869   for ( int t = 0; t < 2; t++ ) {
2870     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2871     int i = 0, iDiag = -1;
2872     while ( it->more()) {
2873       const SMDS_MeshElement *n = it->next();
2874       if ( n == n1 || n == n2 ) {
2875         if ( iDiag < 0)
2876           iDiag = i;
2877         else {
2878           if ( i - iDiag == 1 )
2879             nFirst[ t ] = ( n == n1 ? n2 : n1 );
2880           else
2881             nFirst[ t ] = n;
2882           break;
2883         }
2884       }
2885       i++;
2886     }
2887   }
2888   if ( nFirst[ 0 ] == nFirst[ 1 ] )
2889     N2.Reverse();
2890
2891   angle = N1.Angle( N2 );
2892   //SCRUTE( angle );
2893   return angle;
2894 }
2895
2896 // =================================================
2897 // class generating a unique ID for a pair of nodes
2898 // and able to return nodes by that ID
2899 // =================================================
2900 class LinkID_Gen {
2901 public:
2902
2903   LinkID_Gen( const SMESHDS_Mesh* theMesh )
2904     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2905   {}
2906
2907   long GetLinkID (const SMDS_MeshNode * n1,
2908                   const SMDS_MeshNode * n2) const
2909   {
2910     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2911   }
2912
2913   bool GetNodes (const long             theLinkID,
2914                  const SMDS_MeshNode* & theNode1,
2915                  const SMDS_MeshNode* & theNode2) const
2916   {
2917     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2918     if ( !theNode1 ) return false;
2919     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2920     if ( !theNode2 ) return false;
2921     return true;
2922   }
2923
2924 private:
2925   LinkID_Gen();
2926   const SMESHDS_Mesh* myMesh;
2927   long                myMaxID;
2928 };
2929
2930
2931 //=======================================================================
2932 //function : TriToQuad
2933 //purpose  : Fuse neighbour triangles into quadrangles.
2934 //           theCrit is used to select a neighbour to fuse with.
2935 //           theMaxAngle is a max angle between element normals at which
2936 //           fusion is still performed.
2937 //=======================================================================
2938
2939 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
2940                                   SMESH::Controls::NumericalFunctorPtr theCrit,
2941                                   const double                         theMaxAngle)
2942 {
2943   myLastCreatedElems.Clear();
2944   myLastCreatedNodes.Clear();
2945
2946   MESSAGE( "::TriToQuad()" );
2947
2948   if ( !theCrit.get() )
2949     return false;
2950
2951   SMESHDS_Mesh * aMesh = GetMeshDS();
2952
2953   // Prepare data for algo: build
2954   // 1. map of elements with their linkIDs
2955   // 2. map of linkIDs with their elements
2956
2957   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2958   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2959   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
2960   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2961
2962   TIDSortedElemSet::iterator itElem;
2963   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2964   {
2965     const SMDS_MeshElement* elem = *itElem;
2966     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2967     bool IsTria = ( elem->NbCornerNodes()==3 );
2968     if (!IsTria) continue;
2969
2970     // retrieve element nodes
2971     const SMDS_MeshNode* aNodes [4];
2972     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2973     int i = 0;
2974     while ( i < 3 )
2975       aNodes[ i++ ] = itN->next();
2976     aNodes[ 3 ] = aNodes[ 0 ];
2977
2978     // fill maps
2979     for ( i = 0; i < 3; i++ ) {
2980       SMESH_TLink link( aNodes[i], aNodes[i+1] );
2981       // check if elements sharing a link can be fused
2982       itLE = mapLi_listEl.find( link );
2983       if ( itLE != mapLi_listEl.end() ) {
2984         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2985           continue;
2986         const SMDS_MeshElement* elem2 = (*itLE).second.front();
2987         //if ( FindShape( elem ) != FindShape( elem2 ))
2988         //  continue; // do not fuse triangles laying on different shapes
2989         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2990           continue; // avoid making badly shaped quads
2991         (*itLE).second.push_back( elem );
2992       }
2993       else {
2994         mapLi_listEl[ link ].push_back( elem );
2995       }
2996       mapEl_setLi [ elem ].insert( link );
2997     }
2998   }
2999   // Clean the maps from the links shared by a sole element, ie
3000   // links to which only one element is bound in mapLi_listEl
3001
3002   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3003     int nbElems = (*itLE).second.size();
3004     if ( nbElems < 2  ) {
3005       const SMDS_MeshElement* elem = (*itLE).second.front();
3006       SMESH_TLink link = (*itLE).first;
3007       mapEl_setLi[ elem ].erase( link );
3008       if ( mapEl_setLi[ elem ].empty() )
3009         mapEl_setLi.erase( elem );
3010     }
3011   }
3012
3013   // Algo: fuse triangles into quadrangles
3014
3015   while ( ! mapEl_setLi.empty() ) {
3016     // Look for the start element:
3017     // the element having the least nb of shared links
3018     const SMDS_MeshElement* startElem = 0;
3019     int minNbLinks = 4;
3020     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3021       int nbLinks = (*itEL).second.size();
3022       if ( nbLinks < minNbLinks ) {
3023         startElem = (*itEL).first;
3024         minNbLinks = nbLinks;
3025         if ( minNbLinks == 1 )
3026           break;
3027       }
3028     }
3029
3030     // search elements to fuse starting from startElem or links of elements
3031     // fused earlyer - startLinks
3032     list< SMESH_TLink > startLinks;
3033     while ( startElem || !startLinks.empty() ) {
3034       while ( !startElem && !startLinks.empty() ) {
3035         // Get an element to start, by a link
3036         SMESH_TLink linkId = startLinks.front();
3037         startLinks.pop_front();
3038         itLE = mapLi_listEl.find( linkId );
3039         if ( itLE != mapLi_listEl.end() ) {
3040           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3041           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3042           for ( ; itE != listElem.end() ; itE++ )
3043             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3044               startElem = (*itE);
3045           mapLi_listEl.erase( itLE );
3046         }
3047       }
3048
3049       if ( startElem ) {
3050         // Get candidates to be fused
3051         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3052         const SMESH_TLink *link12, *link13;
3053         startElem = 0;
3054         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3055         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3056         ASSERT( !setLi.empty() );
3057         set< SMESH_TLink >::iterator itLi;
3058         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3059         {
3060           const SMESH_TLink & link = (*itLi);
3061           itLE = mapLi_listEl.find( link );
3062           if ( itLE == mapLi_listEl.end() )
3063             continue;
3064
3065           const SMDS_MeshElement* elem = (*itLE).second.front();
3066           if ( elem == tr1 )
3067             elem = (*itLE).second.back();
3068           mapLi_listEl.erase( itLE );
3069           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3070             continue;
3071           if ( tr2 ) {
3072             tr3 = elem;
3073             link13 = &link;
3074           }
3075           else {
3076             tr2 = elem;
3077             link12 = &link;
3078           }
3079
3080           // add other links of elem to list of links to re-start from
3081           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3082           set< SMESH_TLink >::iterator it;
3083           for ( it = links.begin(); it != links.end(); it++ ) {
3084             const SMESH_TLink& link2 = (*it);
3085             if ( link2 != link )
3086               startLinks.push_back( link2 );
3087           }
3088         }
3089
3090         // Get nodes of possible quadrangles
3091         const SMDS_MeshNode *n12 [4], *n13 [4];
3092         bool Ok12 = false, Ok13 = false;
3093         const SMDS_MeshNode *linkNode1, *linkNode2;
3094         if(tr2) {
3095           linkNode1 = link12->first;
3096           linkNode2 = link12->second;
3097           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3098             Ok12 = true;
3099         }
3100         if(tr3) {
3101           linkNode1 = link13->first;
3102           linkNode2 = link13->second;
3103           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3104             Ok13 = true;
3105         }
3106
3107         // Choose a pair to fuse
3108         if ( Ok12 && Ok13 ) {
3109           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3110           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3111           double aBadRate12 = getBadRate( &quad12, theCrit );
3112           double aBadRate13 = getBadRate( &quad13, theCrit );
3113           if (  aBadRate13 < aBadRate12 )
3114             Ok12 = false;
3115           else
3116             Ok13 = false;
3117         }
3118
3119         // Make quadrangles
3120         // and remove fused elems and remove links from the maps
3121         mapEl_setLi.erase( tr1 );
3122         if ( Ok12 )
3123         {
3124           mapEl_setLi.erase( tr2 );
3125           mapLi_listEl.erase( *link12 );
3126           if ( tr1->NbNodes() == 3 )
3127           {
3128             const SMDS_MeshElement* newElem = 0;
3129             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3130             myLastCreatedElems.Append(newElem);
3131             AddToSameGroups( newElem, tr1, aMesh );
3132             int aShapeId = tr1->getshapeId();
3133             if ( aShapeId )
3134               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3135             aMesh->RemoveElement( tr1 );
3136             aMesh->RemoveElement( tr2 );
3137           }
3138           else {
3139             vector< const SMDS_MeshNode* > N1;
3140             vector< const SMDS_MeshNode* > N2;
3141             getNodesFromTwoTria(tr1,tr2,N1,N2);
3142             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3143             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3144             // i.e. first nodes from both arrays form a new diagonal
3145             const SMDS_MeshNode* aNodes[8];
3146             aNodes[0] = N1[0];
3147             aNodes[1] = N1[1];
3148             aNodes[2] = N2[0];
3149             aNodes[3] = N2[1];
3150             aNodes[4] = N1[3];
3151             aNodes[5] = N2[5];
3152             aNodes[6] = N2[3];
3153             aNodes[7] = N1[5];
3154             const SMDS_MeshElement* newElem = 0;
3155             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3156               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3157                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3158             else
3159               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3160                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3161             myLastCreatedElems.Append(newElem);
3162             AddToSameGroups( newElem, tr1, aMesh );
3163             int aShapeId = tr1->getshapeId();
3164             if ( aShapeId )
3165               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3166             aMesh->RemoveElement( tr1 );
3167             aMesh->RemoveElement( tr2 );
3168             // remove middle node (9)
3169             if ( N1[4]->NbInverseElements() == 0 )
3170               aMesh->RemoveNode( N1[4] );
3171             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3172               aMesh->RemoveNode( N1[6] );
3173             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3174               aMesh->RemoveNode( N2[6] );
3175           }
3176         }
3177         else if ( Ok13 )
3178         {
3179           mapEl_setLi.erase( tr3 );
3180           mapLi_listEl.erase( *link13 );
3181           if ( tr1->NbNodes() == 3 ) {
3182             const SMDS_MeshElement* newElem = 0;
3183             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3184             myLastCreatedElems.Append(newElem);
3185             AddToSameGroups( newElem, tr1, aMesh );
3186             int aShapeId = tr1->getshapeId();
3187             if ( aShapeId )
3188               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3189             aMesh->RemoveElement( tr1 );
3190             aMesh->RemoveElement( tr3 );
3191           }
3192           else {
3193             vector< const SMDS_MeshNode* > N1;
3194             vector< const SMDS_MeshNode* > N2;
3195             getNodesFromTwoTria(tr1,tr3,N1,N2);
3196             // now we receive following N1 and N2 (using numeration as above image)
3197             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3198             // i.e. first nodes from both arrays form a new diagonal
3199             const SMDS_MeshNode* aNodes[8];
3200             aNodes[0] = N1[0];
3201             aNodes[1] = N1[1];
3202             aNodes[2] = N2[0];
3203             aNodes[3] = N2[1];
3204             aNodes[4] = N1[3];
3205             aNodes[5] = N2[5];
3206             aNodes[6] = N2[3];
3207             aNodes[7] = N1[5];
3208             const SMDS_MeshElement* newElem = 0;
3209             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3210               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3211                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3212             else
3213               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3214                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3215             myLastCreatedElems.Append(newElem);
3216             AddToSameGroups( newElem, tr1, aMesh );
3217             int aShapeId = tr1->getshapeId();
3218             if ( aShapeId )
3219               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3220             aMesh->RemoveElement( tr1 );
3221             aMesh->RemoveElement( tr3 );
3222             // remove middle node (9)
3223             if ( N1[4]->NbInverseElements() == 0 )
3224               aMesh->RemoveNode( N1[4] );
3225             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3226               aMesh->RemoveNode( N1[6] );
3227             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3228               aMesh->RemoveNode( N2[6] );
3229           }
3230         }
3231
3232         // Next element to fuse: the rejected one
3233         if ( tr3 )
3234           startElem = Ok12 ? tr3 : tr2;
3235
3236       } // if ( startElem )
3237     } // while ( startElem || !startLinks.empty() )
3238   } // while ( ! mapEl_setLi.empty() )
3239
3240   return true;
3241 }
3242
3243
3244 /*#define DUMPSO(txt) \
3245 //  cout << txt << endl;
3246 //=============================================================================
3247 //
3248 //
3249 //
3250 //=============================================================================
3251 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3252 {
3253 if ( i1 == i2 )
3254 return;
3255 int tmp = idNodes[ i1 ];
3256 idNodes[ i1 ] = idNodes[ i2 ];
3257 idNodes[ i2 ] = tmp;
3258 gp_Pnt Ptmp = P[ i1 ];
3259 P[ i1 ] = P[ i2 ];
3260 P[ i2 ] = Ptmp;
3261 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3262 }
3263
3264 //=======================================================================
3265 //function : SortQuadNodes
3266 //purpose  : Set 4 nodes of a quadrangle face in a good order.
3267 //           Swap 1<->2 or 2<->3 nodes and correspondingly return
3268 //           1 or 2 else 0.
3269 //=======================================================================
3270
3271 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3272 int               idNodes[] )
3273 {
3274   gp_Pnt P[4];
3275   int i;
3276   for ( i = 0; i < 4; i++ ) {
3277     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3278     if ( !n ) return 0;
3279     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3280   }
3281
3282   gp_Vec V1(P[0], P[1]);
3283   gp_Vec V2(P[0], P[2]);
3284   gp_Vec V3(P[0], P[3]);
3285
3286   gp_Vec Cross1 = V1 ^ V2;
3287   gp_Vec Cross2 = V2 ^ V3;
3288
3289   i = 0;
3290   if (Cross1.Dot(Cross2) < 0)
3291   {
3292     Cross1 = V2 ^ V1;
3293     Cross2 = V1 ^ V3;
3294
3295     if (Cross1.Dot(Cross2) < 0)
3296       i = 2;
3297     else
3298       i = 1;
3299     swap ( i, i + 1, idNodes, P );
3300
3301     //     for ( int ii = 0; ii < 4; ii++ ) {
3302     //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3303     //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3304     //     }
3305   }
3306   return i;
3307 }
3308
3309 //=======================================================================
3310 //function : SortHexaNodes
3311 //purpose  : Set 8 nodes of a hexahedron in a good order.
3312 //           Return success status
3313 //=======================================================================
3314
3315 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3316                                       int               idNodes[] )
3317 {
3318   gp_Pnt P[8];
3319   int i;
3320   DUMPSO( "INPUT: ========================================");
3321   for ( i = 0; i < 8; i++ ) {
3322     const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3323     if ( !n ) return false;
3324     P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3325     DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3326   }
3327   DUMPSO( "========================================");
3328
3329
3330   set<int> faceNodes;  // ids of bottom face nodes, to be found
3331   set<int> checkedId1; // ids of tried 2-nd nodes
3332   Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3333   const Standard_Real tol = 1.e-6;   // tolerance to find nodes in plane
3334   int iMin, iLoop1 = 0;
3335
3336   // Loop to try the 2-nd nodes
3337
3338   while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3339   {
3340     // Find not checked 2-nd node
3341     for ( i = 1; i < 8; i++ )
3342       if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3343         int id1 = idNodes[i];
3344         swap ( 1, i, idNodes, P );
3345         checkedId1.insert ( id1 );
3346         break;
3347       }
3348
3349     // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3350     // ie that all but meybe one (id3 which is on the same face) nodes
3351     // lay on the same side from the triangle plane.
3352
3353     bool manyInPlane = false; // more than 4 nodes lay in plane
3354     int iLoop2 = 0;
3355     while ( ++iLoop2 < 6 ) {
3356
3357       // get 1-2-3 plane coeffs
3358       Standard_Real A, B, C, D;
3359       gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3360       if ( N.SquareMagnitude() > gp::Resolution() )
3361       {
3362         gp_Pln pln ( P[0], N );
3363         pln.Coefficients( A, B, C, D );
3364
3365         // find the node (iMin) closest to pln
3366         Standard_Real dist[ 8 ], minDist = DBL_MAX;
3367         set<int> idInPln;
3368         for ( i = 3; i < 8; i++ ) {
3369           dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3370           if ( fabs( dist[i] ) < minDist ) {
3371             minDist = fabs( dist[i] );
3372             iMin = i;
3373           }
3374           if ( fabs( dist[i] ) <= tol )
3375             idInPln.insert( idNodes[i] );
3376         }
3377
3378         // there should not be more than 4 nodes in bottom plane
3379         if ( idInPln.size() > 1 )
3380         {
3381           DUMPSO( "### idInPln.size() = " << idInPln.size());
3382           // idInPlane does not contain the first 3 nodes
3383           if ( manyInPlane || idInPln.size() == 5)
3384             return false; // all nodes in one plane
3385           manyInPlane = true;
3386
3387           // set the 1-st node to be not in plane
3388           for ( i = 3; i < 8; i++ ) {
3389             if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3390               DUMPSO( "### Reset 0-th node");
3391               swap( 0, i, idNodes, P );
3392               break;
3393             }
3394           }
3395
3396           // reset to re-check second nodes
3397           leastDist = DBL_MAX;
3398           faceNodes.clear();
3399           checkedId1.clear();
3400           iLoop1 = 0;
3401           break; // from iLoop2;
3402         }
3403
3404         // check that the other 4 nodes are on the same side
3405         bool sameSide = true;
3406         bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3407         for ( i = 3; sameSide && i < 8; i++ ) {
3408           if ( i != iMin )
3409             sameSide = ( isNeg == dist[i] <= 0.);
3410         }
3411
3412         // keep best solution
3413         if ( sameSide && minDist < leastDist ) {
3414           leastDist = minDist;
3415           faceNodes.clear();
3416           faceNodes.insert( idNodes[ 1 ] );
3417           faceNodes.insert( idNodes[ 2 ] );
3418           faceNodes.insert( idNodes[ iMin ] );
3419           DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3420                   << " leastDist = " << leastDist);
3421           if ( leastDist <= DBL_MIN )
3422             break;
3423         }
3424       }
3425
3426       // set next 3-d node to check
3427       int iNext = 2 + iLoop2;
3428       if ( iNext < 8 ) {
3429         DUMPSO( "Try 2-nd");
3430         swap ( 2, iNext, idNodes, P );
3431       }
3432     } // while ( iLoop2 < 6 )
3433   } // iLoop1
3434
3435   if ( faceNodes.empty() ) return false;
3436
3437   // Put the faceNodes in proper places
3438   for ( i = 4; i < 8; i++ ) {
3439     if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3440       // find a place to put
3441       int iTo = 1;
3442       while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3443         iTo++;
3444       DUMPSO( "Set faceNodes");
3445       swap ( iTo, i, idNodes, P );
3446     }
3447   }
3448
3449
3450   // Set nodes of the found bottom face in good order
3451   DUMPSO( " Found bottom face: ");
3452   i = SortQuadNodes( theMesh, idNodes );
3453   if ( i ) {
3454     gp_Pnt Ptmp = P[ i ];
3455     P[ i ] = P[ i+1 ];
3456     P[ i+1 ] = Ptmp;
3457   }
3458   //   else
3459   //     for ( int ii = 0; ii < 4; ii++ ) {
3460   //       const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3461   //       DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3462   //    }
3463
3464   // Gravity center of the top and bottom faces
3465   gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3466   gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3467
3468   // Get direction from the bottom to the top face
3469   gp_Vec upDir ( aGCb, aGCt );
3470   Standard_Real upDirSize = upDir.Magnitude();
3471   if ( upDirSize <= gp::Resolution() ) return false;
3472   upDir / upDirSize;
3473
3474   // Assure that the bottom face normal points up
3475   gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3476   Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3477   if ( Nb.Dot( upDir ) < 0 ) {
3478     DUMPSO( "Reverse bottom face");
3479     swap( 1, 3, idNodes, P );
3480   }
3481
3482   // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3483   Standard_Real minDist = DBL_MAX;
3484   for ( i = 4; i < 8; i++ ) {
3485     // projection of P[i] to the plane defined by P[0] and upDir
3486     gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3487     Standard_Real sqDist = P[0].SquareDistance( Pp );
3488     if ( sqDist < minDist ) {
3489       minDist = sqDist;
3490       iMin = i;
3491     }
3492   }
3493   DUMPSO( "Set 4-th");
3494   swap ( 4, iMin, idNodes, P );
3495
3496   // Set nodes of the top face in good order
3497   DUMPSO( "Sort top face");
3498   i = SortQuadNodes( theMesh, &idNodes[4] );
3499   if ( i ) {
3500     i += 4;
3501     gp_Pnt Ptmp = P[ i ];
3502     P[ i ] = P[ i+1 ];
3503     P[ i+1 ] = Ptmp;
3504   }
3505
3506   // Assure that direction of the top face normal is from the bottom face
3507   gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3508   Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3509   if ( Nt.Dot( upDir ) < 0 ) {
3510     DUMPSO( "Reverse top face");
3511     swap( 5, 7, idNodes, P );
3512   }
3513
3514   //   DUMPSO( "OUTPUT: ========================================");
3515   //   for ( i = 0; i < 8; i++ ) {
3516   //     float *p = ugrid->GetPoint(idNodes[i]);
3517   //     DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3518   //   }
3519
3520   return true;
3521 }*/
3522
3523 //================================================================================
3524 /*!
3525  * \brief Return nodes linked to the given one
3526  * \param theNode - the node
3527  * \param linkedNodes - the found nodes
3528  * \param type - the type of elements to check
3529  *
3530  * Medium nodes are ignored
3531  */
3532 //================================================================================
3533
3534 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3535                                        TIDSortedElemSet &   linkedNodes,
3536                                        SMDSAbs_ElementType  type )
3537 {
3538   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3539   while ( elemIt->more() )
3540   {
3541     const SMDS_MeshElement* elem = elemIt->next();
3542     if(elem->GetType() == SMDSAbs_0DElement)
3543       continue;
3544
3545     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3546     if ( elem->GetType() == SMDSAbs_Volume )
3547     {
3548       SMDS_VolumeTool vol( elem );
3549       while ( nodeIt->more() ) {
3550         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3551         if ( theNode != n && vol.IsLinked( theNode, n ))
3552           linkedNodes.insert( n );
3553       }
3554     }
3555     else
3556     {
3557       for ( int i = 0; nodeIt->more(); ++i ) {
3558         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3559         if ( n == theNode ) {
3560           int iBefore = i - 1;
3561           int iAfter  = i + 1;
3562           if ( elem->IsQuadratic() ) {
3563             int nb = elem->NbNodes() / 2;
3564             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3565             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3566           }
3567           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3568           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3569         }
3570       }
3571     }
3572   }
3573 }
3574
3575 //=======================================================================
3576 //function : laplacianSmooth
3577 //purpose  : pulls theNode toward the center of surrounding nodes directly
3578 //           connected to that node along an element edge
3579 //=======================================================================
3580
3581 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3582                      const Handle(Geom_Surface)&          theSurface,
3583                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3584 {
3585   // find surrounding nodes
3586
3587   TIDSortedElemSet nodeSet;
3588   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3589
3590   // compute new coodrs
3591
3592   double coord[] = { 0., 0., 0. };
3593   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3594   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3595     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3596     if ( theSurface.IsNull() ) { // smooth in 3D
3597       coord[0] += node->X();
3598       coord[1] += node->Y();
3599       coord[2] += node->Z();
3600     }
3601     else { // smooth in 2D
3602       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3603       gp_XY* uv = theUVMap[ node ];
3604       coord[0] += uv->X();
3605       coord[1] += uv->Y();
3606     }
3607   }
3608   int nbNodes = nodeSet.size();
3609   if ( !nbNodes )
3610     return;
3611   coord[0] /= nbNodes;
3612   coord[1] /= nbNodes;
3613
3614   if ( !theSurface.IsNull() ) {
3615     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3616     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3617     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3618     coord[0] = p3d.X();
3619     coord[1] = p3d.Y();
3620     coord[2] = p3d.Z();
3621   }
3622   else
3623     coord[2] /= nbNodes;
3624
3625   // move node
3626
3627   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3628 }
3629
3630 //=======================================================================
3631 //function : centroidalSmooth
3632 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3633 //           surrounding elements
3634 //=======================================================================
3635
3636 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3637                       const Handle(Geom_Surface)&          theSurface,
3638                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3639 {
3640   gp_XYZ aNewXYZ(0.,0.,0.);
3641   SMESH::Controls::Area anAreaFunc;
3642   double totalArea = 0.;
3643   int nbElems = 0;
3644
3645   // compute new XYZ
3646
3647   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3648   while ( elemIt->more() )
3649   {
3650     const SMDS_MeshElement* elem = elemIt->next();
3651     nbElems++;
3652
3653     gp_XYZ elemCenter(0.,0.,0.);
3654     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3655     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3656     int nn = elem->NbNodes();
3657     if(elem->IsQuadratic()) nn = nn/2;
3658     int i=0;
3659     //while ( itN->more() ) {
3660     while ( i<nn ) {
3661       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3662       i++;
3663       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3664       aNodePoints.push_back( aP );
3665       if ( !theSurface.IsNull() ) { // smooth in 2D
3666         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3667         gp_XY* uv = theUVMap[ aNode ];
3668         aP.SetCoord( uv->X(), uv->Y(), 0. );
3669       }
3670       elemCenter += aP;
3671     }
3672     double elemArea = anAreaFunc.GetValue( aNodePoints );
3673     totalArea += elemArea;
3674     elemCenter /= nn;
3675     aNewXYZ += elemCenter * elemArea;
3676   }
3677   aNewXYZ /= totalArea;
3678   if ( !theSurface.IsNull() ) {
3679     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3680     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3681   }
3682
3683   // move node
3684
3685   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3686 }
3687
3688 //=======================================================================
3689 //function : getClosestUV
3690 //purpose  : return UV of closest projection
3691 //=======================================================================
3692
3693 static bool getClosestUV (Extrema_GenExtPS& projector,
3694                           const gp_Pnt&     point,
3695                           gp_XY &           result)
3696 {
3697   projector.Perform( point );
3698   if ( projector.IsDone() ) {
3699     double u, v, minVal = DBL_MAX;
3700     for ( int i = projector.NbExt(); i > 0; i-- )
3701       if ( projector.SquareDistance( i ) < minVal ) {
3702         minVal = projector.SquareDistance( i );
3703         projector.Point( i ).Parameter( u, v );
3704       }
3705     result.SetCoord( u, v );
3706     return true;
3707   }
3708   return false;
3709 }
3710
3711 //=======================================================================
3712 //function : Smooth
3713 //purpose  : Smooth theElements during theNbIterations or until a worst
3714 //           element has aspect ratio <= theTgtAspectRatio.
3715 //           Aspect Ratio varies in range [1.0, inf].
3716 //           If theElements is empty, the whole mesh is smoothed.
3717 //           theFixedNodes contains additionally fixed nodes. Nodes built
3718 //           on edges and boundary nodes are always fixed.
3719 //=======================================================================
3720
3721 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3722                                set<const SMDS_MeshNode*> & theFixedNodes,
3723                                const SmoothMethod          theSmoothMethod,
3724                                const int                   theNbIterations,
3725                                double                      theTgtAspectRatio,
3726                                const bool                  the2D)
3727 {
3728   myLastCreatedElems.Clear();
3729   myLastCreatedNodes.Clear();
3730
3731   MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3732
3733   if ( theTgtAspectRatio < 1.0 )
3734     theTgtAspectRatio = 1.0;
3735
3736   const double disttol = 1.e-16;
3737
3738   SMESH::Controls::AspectRatio aQualityFunc;
3739
3740   SMESHDS_Mesh* aMesh = GetMeshDS();
3741
3742   if ( theElems.empty() ) {
3743     // add all faces to theElems
3744     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3745     while ( fIt->more() ) {
3746       const SMDS_MeshElement* face = fIt->next();
3747       theElems.insert( theElems.end(), face );
3748     }
3749   }
3750   // get all face ids theElems are on
3751   set< int > faceIdSet;
3752   TIDSortedElemSet::iterator itElem;
3753   if ( the2D )
3754     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3755       int fId = FindShape( *itElem );
3756       // check that corresponding submesh exists and a shape is face
3757       if (fId &&
3758           faceIdSet.find( fId ) == faceIdSet.end() &&
3759           aMesh->MeshElements( fId )) {
3760         TopoDS_Shape F = aMesh->IndexToShape( fId );
3761         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3762           faceIdSet.insert( fId );
3763       }
3764     }
3765   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3766
3767   // ===============================================
3768   // smooth elements on each TopoDS_Face separately
3769   // ===============================================
3770
3771   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3772   for ( ; fId != faceIdSet.rend(); ++fId ) {
3773     // get face surface and submesh
3774     Handle(Geom_Surface) surface;
3775     SMESHDS_SubMesh* faceSubMesh = 0;
3776     TopoDS_Face face;
3777     double fToler2 = 0, f,l;
3778     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3779     bool isUPeriodic = false, isVPeriodic = false;
3780     if ( *fId ) {
3781       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3782       surface = BRep_Tool::Surface( face );
3783       faceSubMesh = aMesh->MeshElements( *fId );
3784       fToler2 = BRep_Tool::Tolerance( face );
3785       fToler2 *= fToler2 * 10.;
3786       isUPeriodic = surface->IsUPeriodic();
3787       if ( isUPeriodic )
3788         surface->UPeriod();
3789       isVPeriodic = surface->IsVPeriodic();
3790       if ( isVPeriodic )
3791         surface->VPeriod();
3792       surface->Bounds( u1, u2, v1, v2 );
3793     }
3794     // ---------------------------------------------------------
3795     // for elements on a face, find movable and fixed nodes and
3796     // compute UV for them
3797     // ---------------------------------------------------------
3798     bool checkBoundaryNodes = false;
3799     bool isQuadratic = false;
3800     set<const SMDS_MeshNode*> setMovableNodes;
3801     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3802     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3803     list< const SMDS_MeshElement* > elemsOnFace;
3804
3805     Extrema_GenExtPS projector;
3806     GeomAdaptor_Surface surfAdaptor;
3807     if ( !surface.IsNull() ) {
3808       surfAdaptor.Load( surface );
3809       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3810     }
3811     int nbElemOnFace = 0;
3812     itElem = theElems.begin();
3813     // loop on not yet smoothed elements: look for elems on a face
3814     while ( itElem != theElems.end() ) {
3815       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3816         break; // all elements found
3817
3818       const SMDS_MeshElement* elem = *itElem;
3819       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3820            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3821         ++itElem;
3822         continue;
3823       }
3824       elemsOnFace.push_back( elem );
3825       theElems.erase( itElem++ );
3826       nbElemOnFace++;
3827
3828       if ( !isQuadratic )
3829         isQuadratic = elem->IsQuadratic();
3830
3831       // get movable nodes of elem
3832       const SMDS_MeshNode* node;
3833       SMDS_TypeOfPosition posType;
3834       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3835       int nn = 0, nbn =  elem->NbNodes();
3836       if(elem->IsQuadratic())
3837         nbn = nbn/2;
3838       while ( nn++ < nbn ) {
3839         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3840         const SMDS_PositionPtr& pos = node->GetPosition();
3841         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3842         if (posType != SMDS_TOP_EDGE &&
3843             posType != SMDS_TOP_VERTEX &&
3844             theFixedNodes.find( node ) == theFixedNodes.end())
3845         {
3846           // check if all faces around the node are on faceSubMesh
3847           // because a node on edge may be bound to face
3848           SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3849           bool all = true;
3850           if ( faceSubMesh ) {
3851             while ( eIt->more() && all ) {
3852               const SMDS_MeshElement* e = eIt->next();
3853               all = faceSubMesh->Contains( e );
3854             }
3855           }
3856           if ( all )
3857             setMovableNodes.insert( node );
3858           else
3859             checkBoundaryNodes = true;
3860         }
3861         if ( posType == SMDS_TOP_3DSPACE )
3862           checkBoundaryNodes = true;
3863       }
3864
3865       if ( surface.IsNull() )
3866         continue;
3867
3868       // get nodes to check UV
3869       list< const SMDS_MeshNode* > uvCheckNodes;
3870       itN = elem->nodesIterator();
3871       nn = 0; nbn =  elem->NbNodes();
3872       if(elem->IsQuadratic())
3873         nbn = nbn/2;
3874       while ( nn++ < nbn ) {
3875         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3876         if ( uvMap.find( node ) == uvMap.end() )
3877           uvCheckNodes.push_back( node );
3878         // add nodes of elems sharing node
3879         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3880         //         while ( eIt->more() ) {
3881         //           const SMDS_MeshElement* e = eIt->next();
3882         //           if ( e != elem ) {
3883         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3884         //             while ( nIt->more() ) {
3885         //               const SMDS_MeshNode* n =
3886         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3887         //               if ( uvMap.find( n ) == uvMap.end() )
3888         //                 uvCheckNodes.push_back( n );
3889         //             }
3890         //           }
3891         //         }
3892       }
3893       // check UV on face
3894       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3895       for ( ; n != uvCheckNodes.end(); ++n ) {
3896         node = *n;
3897         gp_XY uv( 0, 0 );
3898         const SMDS_PositionPtr& pos = node->GetPosition();
3899         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3900         // get existing UV
3901         switch ( posType ) {
3902         case SMDS_TOP_FACE: {
3903           SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3904           uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3905           break;
3906         }
3907         case SMDS_TOP_EDGE: {
3908           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3909           Handle(Geom2d_Curve) pcurve;
3910           if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3911             pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3912           if ( !pcurve.IsNull() ) {
3913             double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3914             uv = pcurve->Value( u ).XY();
3915           }
3916           break;
3917         }
3918         case SMDS_TOP_VERTEX: {
3919           TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3920           if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3921             uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3922           break;
3923         }
3924         default:;
3925         }
3926         // check existing UV
3927         bool project = true;
3928         gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3929         double dist1 = DBL_MAX, dist2 = 0;
3930         if ( posType != SMDS_TOP_3DSPACE ) {
3931           dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3932           project = dist1 > fToler2;
3933         }
3934         if ( project ) { // compute new UV
3935           gp_XY newUV;
3936           if ( !getClosestUV( projector, pNode, newUV )) {
3937             MESSAGE("Node Projection Failed " << node);
3938           }
3939           else {
3940             if ( isUPeriodic )
3941               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3942             if ( isVPeriodic )
3943               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3944             // check new UV
3945             if ( posType != SMDS_TOP_3DSPACE )
3946               dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3947             if ( dist2 < dist1 )
3948               uv = newUV;
3949           }
3950         }
3951         // store UV in the map
3952         listUV.push_back( uv );
3953         uvMap.insert( make_pair( node, &listUV.back() ));
3954       }
3955     } // loop on not yet smoothed elements
3956
3957     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3958       checkBoundaryNodes = true;
3959
3960     // fix nodes on mesh boundary
3961
3962     if ( checkBoundaryNodes ) {
3963       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3964       map< SMESH_TLink, int >::iterator link_nb;
3965       // put all elements links to linkNbMap
3966       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3967       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3968         const SMDS_MeshElement* elem = (*elemIt);
3969         int nbn =  elem->NbCornerNodes();
3970         // loop on elem links: insert them in linkNbMap
3971         for ( int iN = 0; iN < nbn; ++iN ) {
3972           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3973           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3974           SMESH_TLink link( n1, n2 );
3975           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3976           link_nb->second++;
3977         }
3978       }
3979       // remove nodes that are in links encountered only once from setMovableNodes
3980       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3981         if ( link_nb->second == 1 ) {
3982           setMovableNodes.erase( link_nb->first.node1() );
3983           setMovableNodes.erase( link_nb->first.node2() );
3984         }
3985       }
3986     }
3987
3988     // -----------------------------------------------------
3989     // for nodes on seam edge, compute one more UV ( uvMap2 );
3990     // find movable nodes linked to nodes on seam and which
3991     // are to be smoothed using the second UV ( uvMap2 )
3992     // -----------------------------------------------------
3993
3994     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3995     if ( !surface.IsNull() ) {
3996       TopExp_Explorer eExp( face, TopAbs_EDGE );
3997       for ( ; eExp.More(); eExp.Next() ) {
3998         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3999         if ( !BRep_Tool::IsClosed( edge, face ))
4000           continue;
4001         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4002         if ( !sm ) continue;
4003         // find out which parameter varies for a node on seam
4004         double f,l;
4005         gp_Pnt2d uv1, uv2;
4006         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4007         if ( pcurve.IsNull() ) continue;
4008         uv1 = pcurve->Value( f );
4009         edge.Reverse();
4010         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4011         if ( pcurve.IsNull() ) continue;
4012         uv2 = pcurve->Value( f );
4013         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4014         // assure uv1 < uv2
4015         if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
4016           gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
4017         }
4018         // get nodes on seam and its vertices
4019         list< const SMDS_MeshNode* > seamNodes;
4020         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4021         while ( nSeamIt->more() ) {
4022           const SMDS_MeshNode* node = nSeamIt->next();
4023           if ( !isQuadratic || !IsMedium( node ))
4024             seamNodes.push_back( node );
4025         }
4026         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4027         for ( ; vExp.More(); vExp.Next() ) {
4028           sm = aMesh->MeshElements( vExp.Current() );
4029           if ( sm ) {
4030             nSeamIt = sm->GetNodes();
4031             while ( nSeamIt->more() )
4032               seamNodes.push_back( nSeamIt->next() );
4033           }
4034         }
4035         // loop on nodes on seam
4036         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4037         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4038           const SMDS_MeshNode* nSeam = *noSeIt;
4039           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4040           if ( n_uv == uvMap.end() )
4041             continue;
4042           // set the first UV
4043           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4044           // set the second UV
4045           listUV.push_back( *n_uv->second );
4046           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4047           if ( uvMap2.empty() )
4048             uvMap2 = uvMap; // copy the uvMap contents
4049           uvMap2[ nSeam ] = &listUV.back();
4050
4051           // collect movable nodes linked to ones on seam in nodesNearSeam
4052           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4053           while ( eIt->more() ) {
4054             const SMDS_MeshElement* e = eIt->next();
4055             int nbUseMap1 = 0, nbUseMap2 = 0;
4056             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4057             int nn = 0, nbn =  e->NbNodes();
4058             if(e->IsQuadratic()) nbn = nbn/2;
4059             while ( nn++ < nbn )
4060             {
4061               const SMDS_MeshNode* n =
4062                 static_cast<const SMDS_MeshNode*>( nIt->next() );
4063               if (n == nSeam ||
4064                   setMovableNodes.find( n ) == setMovableNodes.end() )
4065                 continue;
4066               // add only nodes being closer to uv2 than to uv1
4067               gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4068                            0.5 * ( n->Y() + nSeam->Y() ),
4069                            0.5 * ( n->Z() + nSeam->Z() ));
4070               gp_XY uv;
4071               getClosestUV( projector, pMid, uv );
4072               if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
4073                 nodesNearSeam.insert( n );
4074                 nbUseMap2++;
4075               }
4076               else
4077                 nbUseMap1++;
4078             }
4079             // for centroidalSmooth all element nodes must
4080             // be on one side of a seam
4081             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4082               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4083               nn = 0;
4084               while ( nn++ < nbn ) {
4085                 const SMDS_MeshNode* n =
4086                   static_cast<const SMDS_MeshNode*>( nIt->next() );
4087                 setMovableNodes.erase( n );
4088               }
4089             }
4090           }
4091         } // loop on nodes on seam
4092       } // loop on edge of a face
4093     } // if ( !face.IsNull() )
4094
4095     if ( setMovableNodes.empty() ) {
4096       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4097       continue; // goto next face
4098     }
4099
4100     // -------------
4101     // SMOOTHING //
4102     // -------------
4103
4104     int it = -1;
4105     double maxRatio = -1., maxDisplacement = -1.;
4106     set<const SMDS_MeshNode*>::iterator nodeToMove;
4107     for ( it = 0; it < theNbIterations; it++ ) {
4108       maxDisplacement = 0.;
4109       nodeToMove = setMovableNodes.begin();
4110       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4111         const SMDS_MeshNode* node = (*nodeToMove);
4112         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4113
4114         // smooth
4115         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4116         if ( theSmoothMethod == LAPLACIAN )
4117           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4118         else
4119           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4120
4121         // node displacement
4122         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4123         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4124         if ( aDispl > maxDisplacement )
4125           maxDisplacement = aDispl;
4126       }
4127       // no node movement => exit
4128       //if ( maxDisplacement < 1.e-16 ) {
4129       if ( maxDisplacement < disttol ) {
4130         MESSAGE("-- no node movement --");
4131         break;
4132       }
4133
4134       // check elements quality
4135       maxRatio  = 0;
4136       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4137       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4138         const SMDS_MeshElement* elem = (*elemIt);
4139         if ( !elem || elem->GetType() != SMDSAbs_Face )
4140           continue;
4141         SMESH::Controls::TSequenceOfXYZ aPoints;
4142         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4143           double aValue = aQualityFunc.GetValue( aPoints );
4144           if ( aValue > maxRatio )
4145             maxRatio = aValue;
4146         }
4147       }
4148       if ( maxRatio <= theTgtAspectRatio ) {
4149         MESSAGE("-- quality achived --");
4150         break;
4151       }
4152       if (it+1 == theNbIterations) {
4153         MESSAGE("-- Iteration limit exceeded --");
4154       }
4155     } // smoothing iterations
4156
4157     MESSAGE(" Face id: " << *fId <<
4158             " Nb iterstions: " << it <<
4159             " Displacement: " << maxDisplacement <<
4160             " Aspect Ratio " << maxRatio);
4161
4162     // ---------------------------------------
4163     // new nodes positions are computed,
4164     // record movement in DS and set new UV
4165     // ---------------------------------------
4166     nodeToMove = setMovableNodes.begin();
4167     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4168       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4169       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4170       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4171       if ( node_uv != uvMap.end() ) {
4172         gp_XY* uv = node_uv->second;
4173         node->SetPosition
4174           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4175       }
4176     }
4177
4178     // move medium nodes of quadratic elements
4179     if ( isQuadratic )
4180     {
4181       SMESH_MesherHelper helper( *GetMesh() );
4182       helper.SetSubShape( face );
4183       vector<const SMDS_MeshNode*> nodes;
4184       bool checkUV;
4185       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4186       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4187       {
4188         const SMDS_MeshElement* QF = *elemIt;
4189         if ( QF->IsQuadratic() )
4190         {
4191           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4192                         SMDS_MeshElement::iterator() );
4193           nodes.push_back( nodes[0] );
4194           gp_Pnt xyz;
4195           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4196           {
4197             if ( !surface.IsNull() )
4198             {
4199               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4200               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4201               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4202               xyz = surface->Value( uv.X(), uv.Y() );
4203             }
4204             else {
4205               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4206             }
4207             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4208               // we have to move a medium node
4209               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4210           }
4211         }
4212       }
4213     }
4214
4215   } // loop on face ids
4216
4217 }
4218
4219 //=======================================================================
4220 //function : isReverse
4221 //purpose  : Return true if normal of prevNodes is not co-directied with
4222 //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4223 //           iNotSame is where prevNodes and nextNodes are different.
4224 //           If result is true then future volume orientation is OK
4225 //=======================================================================
4226
4227 static bool isReverse(const SMDS_MeshElement*             face,
4228                       const vector<const SMDS_MeshNode*>& prevNodes,
4229                       const vector<const SMDS_MeshNode*>& nextNodes,
4230                       const int                           iNotSame)
4231 {
4232
4233   SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4234   SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4235   gp_XYZ extrDir( pN - pP ), faceNorm;
4236   SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4237
4238   return faceNorm * extrDir < 0.0;
4239 }
4240
4241 //=======================================================================
4242 /*!
4243  * \brief Create elements by sweeping an element
4244  * \param elem - element to sweep
4245  * \param newNodesItVec - nodes generated from each node of the element
4246  * \param newElems - generated elements
4247  * \param nbSteps - number of sweeping steps
4248  * \param srcElements - to append elem for each generated element
4249  */
4250 //=======================================================================
4251
4252 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4253                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4254                                     list<const SMDS_MeshElement*>&        newElems,
4255                                     const int                             nbSteps,
4256                                     SMESH_SequenceOfElemPtr&              srcElements)
4257 {
4258   //MESSAGE("sweepElement " << nbSteps);
4259   SMESHDS_Mesh* aMesh = GetMeshDS();
4260
4261   const int           nbNodes = elem->NbNodes();
4262   const int         nbCorners = elem->NbCornerNodes();
4263   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4264                                                           polyhedron creation !!! */
4265   // Loop on elem nodes:
4266   // find new nodes and detect same nodes indices
4267   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4268   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4269   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4270   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4271
4272   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4273   vector<int> sames(nbNodes);
4274   vector<bool> isSingleNode(nbNodes);
4275
4276   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4277     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4278     const SMDS_MeshNode*                         node = nnIt->first;
4279     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4280     if ( listNewNodes.empty() )
4281       return;
4282
4283     itNN   [ iNode ] = listNewNodes.begin();
4284     prevNod[ iNode ] = node;
4285     nextNod[ iNode ] = listNewNodes.front();
4286
4287     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4288                                                              corner node of linear */
4289     if ( prevNod[ iNode ] != nextNod [ iNode ])
4290       nbDouble += !isSingleNode[iNode];
4291
4292     if( iNode < nbCorners ) { // check corners only
4293       if ( prevNod[ iNode ] == nextNod [ iNode ])
4294         sames[nbSame++] = iNode;
4295       else
4296         iNotSameNode = iNode;
4297     }
4298   }
4299
4300   if ( nbSame == nbNodes || nbSame > 2) {
4301     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4302     return;
4303   }
4304
4305   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4306   {
4307     // fix nodes order to have bottom normal external
4308     if ( baseType == SMDSEntity_Polygon )
4309     {
4310       std::reverse( itNN.begin(), itNN.end() );
4311       std::reverse( prevNod.begin(), prevNod.end() );
4312       std::reverse( midlNod.begin(), midlNod.end() );
4313       std::reverse( nextNod.begin(), nextNod.end() );
4314       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4315     }
4316     else
4317     {
4318       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4319       SMDS_MeshCell::applyInterlace( ind, itNN );
4320       SMDS_MeshCell::applyInterlace( ind, prevNod );
4321       SMDS_MeshCell::applyInterlace( ind, nextNod );
4322       SMDS_MeshCell::applyInterlace( ind, midlNod );
4323       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4324       if ( nbSame > 0 )
4325       {
4326         sames[nbSame] = iNotSameNode;
4327         for ( int j = 0; j <= nbSame; ++j )
4328           for ( size_t i = 0; i < ind.size(); ++i )
4329             if ( ind[i] == sames[j] )
4330             {
4331               sames[j] = i;
4332               break;
4333             }
4334         iNotSameNode = sames[nbSame];
4335       }
4336     }
4337   }
4338   else if ( elem->GetType() == SMDSAbs_Edge )
4339   {
4340     // orient a new face same as adjacent one
4341     int i1, i2;
4342     const SMDS_MeshElement* e;
4343     TIDSortedElemSet dummy;
4344     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4345         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4346         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4347     {
4348       // there is an adjacent face, check order of nodes in it
4349       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4350       if ( sameOrder )
4351       {
4352         std::swap( itNN[0],    itNN[1] );
4353         std::swap( prevNod[0], prevNod[1] );
4354         std::swap( nextNod[0], nextNod[1] );
4355         if ( nbSame > 0 )
4356           sames[0] = 1 - sames[0];
4357         iNotSameNode = 1 - iNotSameNode;
4358       }
4359     }
4360   }
4361
4362   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4363   if ( nbSame > 0 ) {
4364     iSameNode    = sames[ nbSame-1 ];
4365     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4366     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4367     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4368   }
4369
4370   // make new elements
4371   for (int iStep = 0; iStep < nbSteps; iStep++ )
4372   {
4373     // get next nodes
4374     for ( iNode = 0; iNode < nbNodes; iNode++ )
4375     {
4376       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4377       nextNod[ iNode ] = *itNN[ iNode ]++;
4378     }
4379
4380     SMDS_MeshElement* aNewElem = 0;
4381     /*if(!elem->IsPoly())*/ {
4382       switch ( baseType ) {
4383       case SMDSEntity_0D:
4384       case SMDSEntity_Node: { // sweep NODE
4385         if ( nbSame == 0 ) {
4386           if ( isSingleNode[0] )
4387             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4388           else
4389             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4390         }
4391         else
4392           return;
4393         break;
4394       }
4395       case SMDSEntity_Edge: { // sweep EDGE
4396         if ( nbDouble == 0 )
4397         {
4398           if ( nbSame == 0 ) // ---> quadrangle
4399             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4400                                       nextNod[ 1 ], nextNod[ 0 ] );
4401           else               // ---> triangle
4402             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4403                                       nextNod[ iNotSameNode ] );
4404         }
4405         else                 // ---> polygon
4406         {
4407           vector<const SMDS_MeshNode*> poly_nodes;
4408           poly_nodes.push_back( prevNod[0] );
4409           poly_nodes.push_back( prevNod[1] );
4410           if ( prevNod[1] != nextNod[1] )
4411           {
4412             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4413             poly_nodes.push_back( nextNod[1] );
4414           }
4415           if ( prevNod[0] != nextNod[0] )
4416           {
4417             poly_nodes.push_back( nextNod[0] );
4418             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4419           }
4420           switch ( poly_nodes.size() ) {
4421           case 3:
4422             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4423             break;
4424           case 4:
4425             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4426                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4427             break;
4428           default:
4429             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4430           }
4431         }
4432         break;
4433       }
4434       case SMDSEntity_Triangle: // TRIANGLE --->
4435         {
4436           if ( nbDouble > 0 ) break;
4437           if ( nbSame == 0 )       // ---> pentahedron
4438             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4439                                          nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4440
4441           else if ( nbSame == 1 )  // ---> pyramid
4442             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4443                                          nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4444                                          nextNod[ iSameNode ]);
4445
4446           else // 2 same nodes:       ---> tetrahedron
4447             aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4448                                          nextNod[ iNotSameNode ]);
4449           break;
4450         }
4451       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4452         {
4453           if ( nbSame == 2 )
4454             return;
4455           if ( nbDouble+nbSame == 2 )
4456           {
4457             if(nbSame==0) {      // ---> quadratic quadrangle
4458               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4459                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4460             }
4461             else { //(nbSame==1) // ---> quadratic triangle
4462               if(sames[0]==2) {
4463                 return; // medium node on axis
4464               }
4465               else if(sames[0]==0)
4466                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4467                                           prevNod[2], midlNod[1], nextNod[2] );
4468               else // sames[0]==1
4469                 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4470                                           prevNod[2], nextNod[2], midlNod[0]);
4471             }
4472           }
4473           else if ( nbDouble == 3 )
4474           {
4475             if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4476               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4477                                         prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4478             }
4479           }
4480           else
4481             return;
4482           break;
4483         }
4484       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4485         if ( nbDouble > 0 ) break;
4486
4487         if ( nbSame == 0 )       // ---> hexahedron
4488           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4489                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4490
4491         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4492           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4493                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4494                                        nextNod[ iSameNode ]);
4495           newElems.push_back( aNewElem );
4496           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4497                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4498                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4499         }
4500         else if ( nbSame == 2 ) { // ---> pentahedron
4501           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4502             // iBeforeSame is same too
4503             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4504                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4505                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4506           else
4507             // iAfterSame is same too
4508             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4509                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4510                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4511         }
4512         break;
4513       }
4514       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4515       case SMDSEntity_BiQuad_Triangle: /* ??? */ { 
4516         if ( nbDouble+nbSame != 3 ) break;
4517         if(nbSame==0) {
4518           // --->  pentahedron with 15 nodes
4519           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4520                                        nextNod[0], nextNod[1], nextNod[2],
4521                                        prevNod[3], prevNod[4], prevNod[5],
4522                                        nextNod[3], nextNod[4], nextNod[5],
4523                                        midlNod[0], midlNod[1], midlNod[2]);
4524         }
4525         else if(nbSame==1) {
4526           // --->  2d order pyramid of 13 nodes
4527           int apex = iSameNode;
4528           int i0 = ( apex + 1 ) % nbCorners;
4529           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4530           int i0a = apex + 3;
4531           int i1a = i1 + 3;
4532           int i01 = i0 + 3;
4533           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4534                                       nextNod[i0], nextNod[i1], prevNod[apex],
4535                                       prevNod[i01], midlNod[i0],
4536                                       nextNod[i01], midlNod[i1],
4537                                       prevNod[i1a], prevNod[i0a],
4538                                       nextNod[i0a], nextNod[i1a]);
4539         }
4540         else if(nbSame==2) {
4541           // --->  2d order tetrahedron of 10 nodes
4542           int n1 = iNotSameNode;
4543           int n2 = ( n1 + 1             ) % nbCorners;
4544           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4545           int n12 = n1 + 3;
4546           int n23 = n2 + 3;
4547           int n31 = n3 + 3;
4548           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4549                                        prevNod[n12], prevNod[n23], prevNod[n31],
4550                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4551         }
4552         break;
4553       }
4554       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4555         if( nbSame == 0 ) {
4556           if ( nbDouble != 4 ) break;
4557           // --->  hexahedron with 20 nodes
4558           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4559                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4560                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4561                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4562                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4563         }
4564         else if(nbSame==1) {
4565           // ---> pyramid + pentahedron - can not be created since it is needed
4566           // additional middle node at the center of face
4567           INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4568           return;
4569         }
4570         else if( nbSame == 2 ) {
4571           if ( nbDouble != 2 ) break;
4572           // --->  2d order Pentahedron with 15 nodes
4573           int n1,n2,n4,n5;
4574           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4575             // iBeforeSame is same too
4576             n1 = iBeforeSame;
4577             n2 = iOpposSame;
4578             n4 = iSameNode;
4579             n5 = iAfterSame;
4580           }
4581           else {
4582             // iAfterSame is same too
4583             n1 = iSameNode;
4584             n2 = iBeforeSame;
4585             n4 = iAfterSame;
4586             n5 = iOpposSame;
4587           }
4588           int n12 = n2 + 4;
4589           int n45 = n4 + 4;
4590           int n14 = n1 + 4;
4591           int n25 = n5 + 4;
4592           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4593                                        prevNod[n4], prevNod[n5], nextNod[n5],
4594                                        prevNod[n12], midlNod[n2], nextNod[n12],
4595                                        prevNod[n45], midlNod[n5], nextNod[n45],
4596                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4597         }
4598         break;
4599       }
4600       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4601
4602         if( nbSame == 0 && nbDouble == 9 ) {
4603           // --->  tri-quadratic hexahedron with 27 nodes
4604           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4605                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4606                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4607                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4608                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4609                                        prevNod[8], // bottom center
4610                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4611                                        nextNod[8], // top center
4612                                        midlNod[8]);// elem center
4613         }
4614         else
4615         {
4616           return;
4617         }
4618         break;
4619       }
4620       case SMDSEntity_Polygon: { // sweep POLYGON
4621
4622         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4623           // --->  hexagonal prism
4624           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4625                                        prevNod[3], prevNod[4], prevNod[5],
4626                                        nextNod[0], nextNod[1], nextNod[2],
4627                                        nextNod[3], nextNod[4], nextNod[5]);
4628         }
4629         break;
4630       }
4631       case SMDSEntity_Ball:
4632         return;
4633
4634       default:
4635         break;
4636       } // switch ( baseType )
4637     } // scope
4638
4639     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4640     {
4641       if ( baseType != SMDSEntity_Polygon )
4642       {
4643         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4644         SMDS_MeshCell::applyInterlace( ind, prevNod );
4645         SMDS_MeshCell::applyInterlace( ind, nextNod );
4646         SMDS_MeshCell::applyInterlace( ind, midlNod );
4647         SMDS_MeshCell::applyInterlace( ind, itNN );
4648         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4649         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4650       }
4651       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4652       vector<int> quantities (nbNodes + 2);
4653       polyedre_nodes.clear();
4654       quantities.clear();
4655
4656       // bottom of prism
4657       for (int inode = 0; inode < nbNodes; inode++)
4658         polyedre_nodes.push_back( prevNod[inode] );
4659       quantities.push_back( nbNodes );
4660
4661       // top of prism
4662       polyedre_nodes.push_back( nextNod[0] );
4663       for (int inode = nbNodes; inode-1; --inode )
4664         polyedre_nodes.push_back( nextNod[inode-1] );
4665       quantities.push_back( nbNodes );
4666
4667       // side faces
4668       for (int iface = 0; iface < nbNodes; iface++)
4669       {
4670         const int prevNbNodes = polyedre_nodes.size();
4671         int inextface = (iface+1) % nbNodes;
4672         polyedre_nodes.push_back( prevNod[inextface] );
4673         polyedre_nodes.push_back( prevNod[iface] );
4674         if ( prevNod[iface] != nextNod[iface] )
4675         {
4676           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4677           polyedre_nodes.push_back( nextNod[iface] );
4678         }
4679         if ( prevNod[inextface] != nextNod[inextface] )
4680         {
4681           polyedre_nodes.push_back( nextNod[inextface] );
4682           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4683         }
4684         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4685         if ( nbFaceNodes > 2 )
4686           quantities.push_back( nbFaceNodes );
4687         else // degenerated face
4688           polyedre_nodes.resize( prevNbNodes );
4689       }
4690       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4691
4692     } //  // try to create a polyherdal prism
4693
4694     if ( aNewElem ) {
4695       newElems.push_back( aNewElem );
4696       myLastCreatedElems.Append(aNewElem);
4697       srcElements.Append( elem );
4698     }
4699
4700     // set new prev nodes
4701     for ( iNode = 0; iNode < nbNodes; iNode++ )
4702       prevNod[ iNode ] = nextNod[ iNode ];
4703
4704   } // loop on steps
4705 }
4706
4707 //=======================================================================
4708 /*!
4709  * \brief Create 1D and 2D elements around swept elements
4710  * \param mapNewNodes - source nodes and ones generated from them
4711  * \param newElemsMap - source elements and ones generated from them
4712  * \param elemNewNodesMap - nodes generated from each node of each element
4713  * \param elemSet - all swept elements
4714  * \param nbSteps - number of sweeping steps
4715  * \param srcElements - to append elem for each generated element
4716  */
4717 //=======================================================================
4718
4719 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4720                                   TTElemOfElemListMap &    newElemsMap,
4721                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4722                                   TIDSortedElemSet&        elemSet,
4723                                   const int                nbSteps,
4724                                   SMESH_SequenceOfElemPtr& srcElements)
4725 {
4726   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4727   SMESHDS_Mesh* aMesh = GetMeshDS();
4728
4729   // Find nodes belonging to only one initial element - sweep them into edges.
4730
4731   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4732   for ( ; nList != mapNewNodes.end(); nList++ )
4733   {
4734     const SMDS_MeshNode* node =
4735       static_cast<const SMDS_MeshNode*>( nList->first );
4736     if ( newElemsMap.count( node ))
4737       continue; // node was extruded into edge
4738     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4739     int nbInitElems = 0;
4740     const SMDS_MeshElement* el = 0;
4741     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4742     while ( eIt->more() && nbInitElems < 2 ) {
4743       el = eIt->next();
4744       SMDSAbs_ElementType type = el->GetType();
4745       if ( type == SMDSAbs_Volume || type < highType ) continue;
4746       if ( type > highType ) {
4747         nbInitElems = 0;
4748         highType = type;
4749       }
4750       nbInitElems += elemSet.count(el);
4751     }
4752     if ( nbInitElems < 2 ) {
4753       bool NotCreateEdge = el && el->IsMediumNode(node);
4754       if(!NotCreateEdge) {
4755         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4756         list<const SMDS_MeshElement*> newEdges;
4757         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4758       }
4759     }
4760   }
4761
4762   // Make a ceiling for each element ie an equal element of last new nodes.
4763   // Find free links of faces - make edges and sweep them into faces.
4764
4765   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4766   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4767   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4768   {
4769     const SMDS_MeshElement* elem = itElem->first;
4770     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4771
4772     if(itElem->second.size()==0) continue;
4773
4774     const bool isQuadratic = elem->IsQuadratic();
4775
4776     if ( elem->GetType() == SMDSAbs_Edge ) {
4777       // create a ceiling edge
4778       if ( !isQuadratic ) {
4779         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4780                                vecNewNodes[ 1 ]->second.back())) {
4781           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4782                                                    vecNewNodes[ 1 ]->second.back()));
4783           srcElements.Append( elem );
4784         }
4785       }
4786       else {
4787         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4788                                vecNewNodes[ 1 ]->second.back(),
4789                                vecNewNodes[ 2 ]->second.back())) {
4790           myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4791                                                    vecNewNodes[ 1 ]->second.back(),
4792                                                    vecNewNodes[ 2 ]->second.back()));
4793           srcElements.Append( elem );
4794         }
4795       }
4796     }
4797     if ( elem->GetType() != SMDSAbs_Face )
4798       continue;
4799
4800     bool hasFreeLinks = false;
4801
4802     TIDSortedElemSet avoidSet;
4803     avoidSet.insert( elem );
4804
4805     set<const SMDS_MeshNode*> aFaceLastNodes;
4806     int iNode, nbNodes = vecNewNodes.size();
4807     if ( !isQuadratic ) {
4808       // loop on the face nodes
4809       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4810         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4811         // look for free links of the face
4812         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4813         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4814         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4815         // check if a link n1-n2 is free
4816         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4817           hasFreeLinks = true;
4818           // make a new edge and a ceiling for a new edge
4819           const SMDS_MeshElement* edge;
4820           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4821             myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4822             srcElements.Append( myLastCreatedElems.Last() );
4823           }
4824           n1 = vecNewNodes[ iNode ]->second.back();
4825           n2 = vecNewNodes[ iNext ]->second.back();
4826           if ( !aMesh->FindEdge( n1, n2 )) {
4827             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4828             srcElements.Append( edge );
4829           }
4830         }
4831       }
4832     }
4833     else { // elem is quadratic face
4834       int nbn = nbNodes/2;
4835       for ( iNode = 0; iNode < nbn; iNode++ ) {
4836         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4837         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4838         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4839         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4840         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4841         // check if a link is free
4842         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4843              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4844              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4845           hasFreeLinks = true;
4846           // make an edge and a ceiling for a new edge
4847           // find medium node
4848           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4849             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4850             srcElements.Append( elem );
4851           }
4852           n1 = vecNewNodes[ iNode ]->second.back();
4853           n2 = vecNewNodes[ iNext ]->second.back();
4854           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4855           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4856             myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4857             srcElements.Append( elem );
4858           }
4859         }
4860       }
4861       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4862         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4863       }
4864     }
4865
4866     // sweep free links into faces
4867
4868     if ( hasFreeLinks )  {
4869       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4870       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4871
4872       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4873       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4874       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4875         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4876         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4877       }
4878       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4879         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4880         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4881       }
4882       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4883         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4884         std::advance( v, volNb );
4885         // find indices of free faces of a volume and their source edges
4886         list< int > freeInd;
4887         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4888         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4889         int iF, nbF = vTool.NbFaces();
4890         for ( iF = 0; iF < nbF; iF ++ ) {
4891           if (vTool.IsFreeFace( iF ) &&
4892               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4893               initNodeSet != faceNodeSet) // except an initial face
4894           {
4895             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4896               continue;
4897             if ( faceNodeSet == initNodeSetNoCenter )
4898               continue;
4899             freeInd.push_back( iF );
4900             // find source edge of a free face iF
4901             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4902             commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4903             std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4904                                    initNodeSet.begin(), initNodeSet.end(),
4905                                    commonNodes.begin());
4906             if ( (*v)->IsQuadratic() )
4907               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4908             else
4909               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4910 #ifdef _DEBUG_
4911             if ( !srcEdges.back() )
4912             {
4913               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4914                    << iF << " of volume #" << vTool.ID() << endl;
4915             }
4916 #endif
4917           }
4918         }
4919         if ( freeInd.empty() )
4920           continue;
4921
4922         // create faces for all steps;
4923         // if such a face has been already created by sweep of edge,
4924         // assure that its orientation is OK
4925         for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4926           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4927           vTool.SetExternalNormal();
4928           const int nextShift = vTool.IsForward() ? +1 : -1;
4929           list< int >::iterator ind = freeInd.begin();
4930           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4931           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4932           {
4933             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4934             int nbn = vTool.NbFaceNodes( *ind );
4935             const SMDS_MeshElement * f = 0;
4936             if ( nbn == 3 )              ///// triangle
4937             {
4938               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4939               if ( !f ||
4940                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4941               {
4942                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4943                                                      nodes[ 1 ],
4944                                                      nodes[ 1 + nextShift ] };
4945                 if ( f )
4946                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4947                 else
4948                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4949                                                             newOrder[ 2 ] ));
4950               }
4951             }
4952             else if ( nbn == 4 )       ///// quadrangle
4953             {
4954               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4955               if ( !f ||
4956                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4957               {
4958                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4959                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4960                 if ( f )
4961                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4962                 else
4963                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4964                                                             newOrder[ 2 ], newOrder[ 3 ]));
4965               }
4966             }
4967             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4968             {
4969               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4970               if ( !f ||
4971                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4972               {
4973                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4974                                                      nodes[2],
4975                                                      nodes[2 + 2*nextShift],
4976                                                      nodes[3 - 2*nextShift],
4977                                                      nodes[3],
4978                                                      nodes[3 + 2*nextShift]};
4979                 if ( f )
4980                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4981                 else
4982                   myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4983                                                             newOrder[ 1 ],
4984                                                             newOrder[ 2 ],
4985                                                             newOrder[ 3 ],
4986                                                             newOrder[ 4 ],
4987                                                             newOrder[ 5 ] ));
4988               }
4989             }
4990             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4991             {
4992               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4993                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4994               if ( !f ||
4995                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4996               {
4997                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4998                                                      nodes[4 - 2*nextShift],
4999                                                      nodes[4],
5000                                                      nodes[4 + 2*nextShift],
5001                                                      nodes[1],
5002                                                      nodes[5 - 2*nextShift],
5003                                                      nodes[5],
5004                                                      nodes[5 + 2*nextShift] };
5005                 if ( f )
5006                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5007                 else
5008                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5009                                                            newOrder[ 2 ], newOrder[ 3 ],
5010                                                            newOrder[ 4 ], newOrder[ 5 ],
5011                                                            newOrder[ 6 ], newOrder[ 7 ]));
5012               }
5013             }
5014             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5015             {
5016               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5017                                       SMDSAbs_Face, /*noMedium=*/false);
5018               if ( !f ||
5019                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5020               {
5021                 const SMDS_MeshNode* newOrder[9] = { 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                                                      nodes[8] };
5030                 if ( f )
5031                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5032                 else
5033                   myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5034                                                            newOrder[ 2 ], newOrder[ 3 ],
5035                                                            newOrder[ 4 ], newOrder[ 5 ],
5036                                                            newOrder[ 6 ], newOrder[ 7 ],
5037                                                            newOrder[ 8 ]));
5038               }
5039             }
5040             else  //////// polygon
5041             {
5042               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5043               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5044               if ( !f ||
5045                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5046               {
5047                 if ( !vTool.IsForward() )
5048                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5049                 if ( f )
5050                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5051                 else
5052                   AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5053               }
5054             }
5055
5056             while ( srcElements.Length() < myLastCreatedElems.Length() )
5057               srcElements.Append( *srcEdge );
5058
5059           }  // loop on free faces
5060
5061           // go to the next volume
5062           iVol = 0;
5063           while ( iVol++ < nbVolumesByStep ) v++;
5064
5065         } // loop on steps
5066       } // loop on volumes of one step
5067     } // sweep free links into faces
5068
5069     // Make a ceiling face with a normal external to a volume
5070
5071     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5072     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5073     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5074
5075     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5076       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5077       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5078     }
5079     if ( iF >= 0 ) {
5080       lastVol.SetExternalNormal();
5081       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5082       int nbn = lastVol.NbFaceNodes( iF );
5083       // we do not use this->AddElement() because nodes are interlaced
5084       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5085       if ( !hasFreeLinks ||
5086            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5087       {
5088         if ( nbn == 3 )
5089           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5090
5091         else if ( nbn == 4 )
5092           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5093
5094         else if ( nbn == 6 && isQuadratic )
5095           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5096                                                     nodes[1], nodes[3], nodes[5]));
5097         else if ( nbn == 7 && isQuadratic )
5098           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5099                                                     nodes[1], nodes[3], nodes[5], nodes[6]));
5100         else if ( nbn == 8 && isQuadratic )
5101           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5102                                                     nodes[1], nodes[3], nodes[5], nodes[7]));
5103         else if ( nbn == 9 && isQuadratic )
5104           myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5105                                                     nodes[1], nodes[3], nodes[5], nodes[7],
5106                                                     nodes[8]));
5107         else
5108           myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5109
5110         while ( srcElements.Length() < myLastCreatedElems.Length() )
5111           srcElements.Append( elem );
5112       }
5113     }
5114   } // loop on swept elements
5115 }
5116
5117 //=======================================================================
5118 //function : RotationSweep
5119 //purpose  :
5120 //=======================================================================
5121
5122 SMESH_MeshEditor::PGroupIDs
5123 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5124                                 const gp_Ax1&      theAxis,
5125                                 const double       theAngle,
5126                                 const int          theNbSteps,
5127                                 const double       theTol,
5128                                 const bool         theMakeGroups,
5129                                 const bool         theMakeWalls)
5130 {
5131   myLastCreatedElems.Clear();
5132   myLastCreatedNodes.Clear();
5133
5134   // source elements for each generated one
5135   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5136
5137   MESSAGE( "RotationSweep()");
5138   gp_Trsf aTrsf;
5139   aTrsf.SetRotation( theAxis, theAngle );
5140   gp_Trsf aTrsf2;
5141   aTrsf2.SetRotation( theAxis, theAngle/2. );
5142
5143   gp_Lin aLine( theAxis );
5144   double aSqTol = theTol * theTol;
5145
5146   SMESHDS_Mesh* aMesh = GetMeshDS();
5147
5148   TNodeOfNodeListMap mapNewNodes;
5149   TElemOfVecOfNnlmiMap mapElemNewNodes;
5150   TTElemOfElemListMap newElemsMap;
5151
5152   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5153                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5154                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5155   // loop on theElems
5156   TIDSortedElemSet::iterator itElem;
5157   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5158     const SMDS_MeshElement* elem = *itElem;
5159     if ( !elem || elem->GetType() == SMDSAbs_Volume )
5160       continue;
5161     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5162     newNodesItVec.reserve( elem->NbNodes() );
5163
5164     // loop on elem nodes
5165     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5166     while ( itN->more() )
5167     {
5168       // check if a node has been already sweeped
5169       const SMDS_MeshNode* node = cast2Node( itN->next() );
5170
5171       gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5172       double coord[3];
5173       aXYZ.Coord( coord[0], coord[1], coord[2] );
5174       bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5175
5176       TNodeOfNodeListMapItr nIt =
5177         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5178       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5179       if ( listNewNodes.empty() )
5180       {
5181         // check if we are to create medium nodes between corner ones
5182         bool needMediumNodes = false;
5183         if ( isQuadraticMesh )
5184         {
5185           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5186           while (it->more() && !needMediumNodes )
5187           {
5188             const SMDS_MeshElement* invElem = it->next();
5189             if ( invElem != elem && !theElems.count( invElem )) continue;
5190             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5191             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5192               needMediumNodes = true;
5193           }
5194         }
5195
5196         // make new nodes
5197         const SMDS_MeshNode * newNode = node;
5198         for ( int i = 0; i < theNbSteps; i++ ) {
5199           if ( !isOnAxis ) {
5200             if ( needMediumNodes )  // create a medium node
5201             {
5202               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5203               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5204               myLastCreatedNodes.Append(newNode);
5205               srcNodes.Append( node );
5206               listNewNodes.push_back( newNode );
5207               aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5208             }
5209             else {
5210               aTrsf.Transforms( coord[0], coord[1], coord[2] );
5211             }
5212             // create a corner node
5213             newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5214             myLastCreatedNodes.Append(newNode);
5215             srcNodes.Append( node );
5216             listNewNodes.push_back( newNode );
5217           }
5218           else {
5219             listNewNodes.push_back( newNode );
5220             // if ( needMediumNodes )
5221             //   listNewNodes.push_back( newNode );
5222           }
5223         }
5224       }
5225       newNodesItVec.push_back( nIt );
5226     }
5227     // make new elements
5228     sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5229   }
5230
5231   if ( theMakeWalls )
5232     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5233
5234   PGroupIDs newGroupIDs;
5235   if ( theMakeGroups )
5236     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5237
5238   return newGroupIDs;
5239 }
5240
5241 //=======================================================================
5242 //function : ExtrusParam
5243 //purpose  : standard construction
5244 //=======================================================================
5245
5246 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&  theStep,
5247                                             const int      theNbSteps,
5248                                             const int      theFlags,
5249                                             const double   theTolerance):
5250   myDir( theStep ),
5251   myFlags( theFlags ),
5252   myTolerance( theTolerance ),
5253   myElemsToUse( NULL )
5254 {
5255   mySteps = new TColStd_HSequenceOfReal;
5256   const double stepSize = theStep.Magnitude();
5257   for (int i=1; i<=theNbSteps; i++ )
5258     mySteps->Append( stepSize );
5259
5260   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5261       ( theTolerance > 0 ))
5262   {
5263     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5264   }
5265   else
5266   {
5267     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5268   }
5269 }
5270
5271 //=======================================================================
5272 //function : ExtrusParam
5273 //purpose  : steps are given explicitly
5274 //=======================================================================
5275
5276 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5277                                             Handle(TColStd_HSequenceOfReal) theSteps,
5278                                             const int                       theFlags,
5279                                             const double                    theTolerance):
5280   myDir( theDir ),
5281   mySteps( theSteps ),
5282   myFlags( theFlags ),
5283   myTolerance( theTolerance ),
5284   myElemsToUse( NULL )
5285 {
5286   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5287       ( theTolerance > 0 ))
5288   {
5289     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5290   }
5291   else
5292   {
5293     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5294   }
5295 }
5296
5297 //=======================================================================
5298 //function : ExtrusParam
5299 //purpose  : for extrusion by normal
5300 //=======================================================================
5301
5302 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5303                                             const int    theNbSteps,
5304                                             const int    theFlags,
5305                                             const int    theDim ):
5306   myDir( 1,0,0 ),
5307   mySteps( new TColStd_HSequenceOfReal ),
5308   myFlags( theFlags ),
5309   myTolerance( 0 ),
5310   myElemsToUse( NULL )
5311 {
5312   for (int i = 0; i < theNbSteps; i++ )
5313     mySteps->Append( theStepSize );
5314
5315   if ( theDim == 1 )
5316   {
5317     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5318   }
5319   else
5320   {
5321     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5322   }
5323 }
5324
5325 //=======================================================================
5326 //function : ExtrusParam::SetElementsToUse
5327 //purpose  : stores elements to use for extrusion by normal, depending on
5328 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5329 //=======================================================================
5330
5331 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5332 {
5333   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5334 }
5335
5336 //=======================================================================
5337 //function : ExtrusParam::beginStepIter
5338 //purpose  : prepare iteration on steps
5339 //=======================================================================
5340
5341 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5342 {
5343   myWithMediumNodes = withMediumNodes;
5344   myNextStep = 1;
5345   myCurSteps.clear();
5346 }
5347 //=======================================================================
5348 //function : ExtrusParam::moreSteps
5349 //purpose  : are there more steps?
5350 //=======================================================================
5351
5352 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5353 {
5354   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5355 }
5356 //=======================================================================
5357 //function : ExtrusParam::nextStep
5358 //purpose  : returns the next step
5359 //=======================================================================
5360
5361 double SMESH_MeshEditor::ExtrusParam::nextStep()
5362 {
5363   double res = 0;
5364   if ( !myCurSteps.empty() )
5365   {
5366     res = myCurSteps.back();
5367     myCurSteps.pop_back();
5368   }
5369   else if ( myNextStep <= mySteps->Length() )
5370   {
5371     myCurSteps.push_back( mySteps->Value( myNextStep ));
5372     ++myNextStep;
5373     if ( myWithMediumNodes )
5374     {
5375       myCurSteps.back() /= 2.;
5376       myCurSteps.push_back( myCurSteps.back() );
5377     }
5378     res = nextStep();
5379   }
5380   return res;
5381 }
5382
5383 //=======================================================================
5384 //function : ExtrusParam::makeNodesByDir
5385 //purpose  : create nodes for standard extrusion
5386 //=======================================================================
5387
5388 int SMESH_MeshEditor::ExtrusParam::
5389 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5390                 const SMDS_MeshNode*              srcNode,
5391                 std::list<const SMDS_MeshNode*> & newNodes,
5392                 const bool                        makeMediumNodes)
5393 {
5394   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5395
5396   int nbNodes = 0;
5397   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5398   {
5399     p += myDir.XYZ() * nextStep();
5400     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5401     newNodes.push_back( newNode );
5402   }
5403   return nbNodes;
5404 }
5405
5406 //=======================================================================
5407 //function : ExtrusParam::makeNodesByDirAndSew
5408 //purpose  : create nodes for standard extrusion with sewing
5409 //=======================================================================
5410
5411 int SMESH_MeshEditor::ExtrusParam::
5412 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5413                       const SMDS_MeshNode*              srcNode,
5414                       std::list<const SMDS_MeshNode*> & newNodes,
5415                       const bool                        makeMediumNodes)
5416 {
5417   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5418
5419   int nbNodes = 0;
5420   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5421   {
5422     P1 += myDir.XYZ() * nextStep();
5423
5424     // try to search in sequence of existing nodes
5425     // if myNodes.Length()>0 we 'nave to use given sequence
5426     // else - use all nodes of mesh
5427     const SMDS_MeshNode * node = 0;
5428     if ( myNodes.Length() > 0 ) {
5429       int i;
5430       for(i=1; i<=myNodes.Length(); i++) {
5431         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5432         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5433         {
5434           node = myNodes.Value(i);
5435           break;
5436         }
5437       }
5438     }
5439     else {
5440       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5441       while(itn->more()) {
5442         SMESH_TNodeXYZ P2( itn->next() );
5443         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5444         {
5445           node = P2._node;
5446           break;
5447         }
5448       }
5449     }
5450
5451     if ( !node )
5452       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5453
5454     newNodes.push_back( node );
5455
5456   } // loop on steps
5457
5458   return nbNodes;
5459 }
5460
5461 //=======================================================================
5462 //function : ExtrusParam::makeNodesByNormal2D
5463 //purpose  : create nodes for extrusion using normals of faces
5464 //=======================================================================
5465
5466 int SMESH_MeshEditor::ExtrusParam::
5467 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5468                      const SMDS_MeshNode*              srcNode,
5469                      std::list<const SMDS_MeshNode*> & newNodes,
5470                      const bool                        makeMediumNodes)
5471 {
5472   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5473
5474   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5475
5476   // get normals to faces sharing srcNode
5477   vector< gp_XYZ > norms, baryCenters;
5478   gp_XYZ norm, avgNorm( 0,0,0 );
5479   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5480   while ( faceIt->more() )
5481   {
5482     const SMDS_MeshElement* face = faceIt->next();
5483     if ( myElemsToUse && !myElemsToUse->count( face ))
5484       continue;
5485     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5486     {
5487       norms.push_back( norm );
5488       avgNorm += norm;
5489       if ( !alongAvgNorm )
5490       {
5491         gp_XYZ bc(0,0,0);
5492         int nbN = 0;
5493         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5494           bc += SMESH_TNodeXYZ( nIt->next() );
5495         baryCenters.push_back( bc / nbN );
5496       }
5497     }
5498   }
5499
5500   if ( norms.empty() ) return 0;
5501
5502   double normSize = avgNorm.Modulus();
5503   if ( normSize < std::numeric_limits<double>::min() )
5504     return 0;
5505
5506   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5507   {
5508     myDir = avgNorm;
5509     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5510   }
5511
5512   avgNorm /= normSize;
5513
5514   int nbNodes = 0;
5515   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5516   {
5517     gp_XYZ pNew = p;
5518     double stepSize = nextStep();
5519
5520     if ( norms.size() > 1 )
5521     {
5522       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5523       {
5524         // translate plane of a face
5525         baryCenters[ iF ] += norms[ iF ] * stepSize;
5526
5527         // find point of intersection of the face plane located at baryCenters[ iF ]
5528         // and avgNorm located at pNew
5529         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5530         double dot  = ( norms[ iF ] * avgNorm );
5531         if ( dot < std::numeric_limits<double>::min() )
5532           dot = stepSize * 1e-3;
5533         double step = -( norms[ iF ] * pNew + d ) / dot;
5534         pNew += step * avgNorm;
5535       }
5536     }
5537     else
5538     {
5539       pNew += stepSize * avgNorm;
5540     }
5541     p = pNew;
5542
5543     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5544     newNodes.push_back( newNode );
5545   }
5546   return nbNodes;
5547 }
5548
5549 //=======================================================================
5550 //function : ExtrusParam::makeNodesByNormal1D
5551 //purpose  : create nodes for extrusion using normals of edges
5552 //=======================================================================
5553
5554 int SMESH_MeshEditor::ExtrusParam::
5555 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5556                      const SMDS_MeshNode*              srcNode,
5557                      std::list<const SMDS_MeshNode*> & newNodes,
5558                      const bool                        makeMediumNodes)
5559 {
5560   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5561   return 0;
5562 }
5563
5564 //=======================================================================
5565 //function : ExtrusionSweep
5566 //purpose  :
5567 //=======================================================================
5568
5569 SMESH_MeshEditor::PGroupIDs
5570 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5571                                   const gp_Vec&        theStep,
5572                                   const int            theNbSteps,
5573                                   TTElemOfElemListMap& newElemsMap,
5574                                   const int            theFlags,
5575                                   const double         theTolerance)
5576 {
5577   ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5578   return ExtrusionSweep( theElems, aParams, newElemsMap );
5579 }
5580
5581
5582 //=======================================================================
5583 //function : ExtrusionSweep
5584 //purpose  :
5585 //=======================================================================
5586
5587 SMESH_MeshEditor::PGroupIDs
5588 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet &   theElems,
5589                                   ExtrusParam&         theParams,
5590                                   TTElemOfElemListMap& newElemsMap)
5591 {
5592   myLastCreatedElems.Clear();
5593   myLastCreatedNodes.Clear();
5594
5595   // source elements for each generated one
5596   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5597
5598   SMESHDS_Mesh* aMesh = GetMeshDS();
5599
5600   const int nbSteps = theParams.NbSteps();
5601   theParams.SetElementsToUse( theElems );
5602
5603   TNodeOfNodeListMap mapNewNodes;
5604   //TNodeOfNodeVecMap mapNewNodes;
5605   TElemOfVecOfNnlmiMap mapElemNewNodes;
5606   //TElemOfVecOfMapNodesMap mapElemNewNodes;
5607
5608   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5609                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5610                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5611   // loop on theElems
5612   TIDSortedElemSet::iterator itElem;
5613   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5614   {
5615     // check element type
5616     const SMDS_MeshElement* elem = *itElem;
5617     if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5618       continue;
5619
5620     const size_t nbNodes = elem->NbNodes();
5621     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5622     newNodesItVec.reserve( nbNodes );
5623
5624     // loop on elem nodes
5625     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5626     while ( itN->more() )
5627     {
5628       // check if a node has been already sweeped
5629       const SMDS_MeshNode* node = cast2Node( itN->next() );
5630       TNodeOfNodeListMap::iterator nIt =
5631         mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5632       list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5633       if ( listNewNodes.empty() )
5634       {
5635         // make new nodes
5636
5637         // check if we are to create medium nodes between corner ones
5638         bool needMediumNodes = false;
5639         if ( isQuadraticMesh )
5640         {
5641           SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5642           while (it->more() && !needMediumNodes )
5643           {
5644             const SMDS_MeshElement* invElem = it->next();
5645             if ( invElem != elem && !theElems.count( invElem )) continue;
5646             needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5647             if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5648               needMediumNodes = true;
5649           }
5650         }
5651         // create nodes for all steps
5652         if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5653         {
5654           list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5655           for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5656           {
5657             myLastCreatedNodes.Append( *newNodesIt );
5658             srcNodes.Append( node );
5659           }
5660         }
5661         else
5662         {
5663           break; // newNodesItVec will be shorter than nbNodes
5664         }
5665       }
5666       newNodesItVec.push_back( nIt );
5667     }
5668     // make new elements
5669     if ( newNodesItVec.size() == nbNodes )
5670       sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5671   }
5672
5673   if ( theParams.ToMakeBoundary() ) {
5674     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbSteps, srcElems );
5675   }
5676   PGroupIDs newGroupIDs;
5677   if ( theParams.ToMakeGroups() )
5678     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5679
5680   return newGroupIDs;
5681 }
5682
5683 //=======================================================================
5684 //function : ExtrusionAlongTrack
5685 //purpose  :
5686 //=======================================================================
5687 SMESH_MeshEditor::Extrusion_Error
5688 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5689                                        SMESH_subMesh*       theTrack,
5690                                        const SMDS_MeshNode* theN1,
5691                                        const bool           theHasAngles,
5692                                        list<double>&        theAngles,
5693                                        const bool           theLinearVariation,
5694                                        const bool           theHasRefPoint,
5695                                        const gp_Pnt&        theRefPoint,
5696                                        const bool           theMakeGroups)
5697 {
5698   MESSAGE("ExtrusionAlongTrack");
5699   myLastCreatedElems.Clear();
5700   myLastCreatedNodes.Clear();
5701
5702   int aNbE;
5703   std::list<double> aPrms;
5704   TIDSortedElemSet::iterator itElem;
5705
5706   gp_XYZ aGC;
5707   TopoDS_Edge aTrackEdge;
5708   TopoDS_Vertex aV1, aV2;
5709
5710   SMDS_ElemIteratorPtr aItE;
5711   SMDS_NodeIteratorPtr aItN;
5712   SMDSAbs_ElementType aTypeE;
5713
5714   TNodeOfNodeListMap mapNewNodes;
5715
5716   // 1. Check data
5717   aNbE = theElements.size();
5718   // nothing to do
5719   if ( !aNbE )
5720     return EXTR_NO_ELEMENTS;
5721
5722   // 1.1 Track Pattern
5723   ASSERT( theTrack );
5724
5725   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5726
5727   aItE = pSubMeshDS->GetElements();
5728   while ( aItE->more() ) {
5729     const SMDS_MeshElement* pE = aItE->next();
5730     aTypeE = pE->GetType();
5731     // Pattern must contain links only
5732     if ( aTypeE != SMDSAbs_Edge )
5733       return EXTR_PATH_NOT_EDGE;
5734   }
5735
5736   list<SMESH_MeshEditor_PathPoint> fullList;
5737
5738   const TopoDS_Shape& aS = theTrack->GetSubShape();
5739   // Sub-shape for the Pattern must be an Edge or Wire
5740   if( aS.ShapeType() == TopAbs_EDGE ) {
5741     aTrackEdge = TopoDS::Edge( aS );
5742     // the Edge must not be degenerated
5743     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5744       return EXTR_BAD_PATH_SHAPE;
5745     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5746     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5747     const SMDS_MeshNode* aN1 = aItN->next();
5748     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5749     const SMDS_MeshNode* aN2 = aItN->next();
5750     // starting node must be aN1 or aN2
5751     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5752       return EXTR_BAD_STARTING_NODE;
5753     aItN = pSubMeshDS->GetNodes();
5754     while ( aItN->more() ) {
5755       const SMDS_MeshNode* pNode = aItN->next();
5756       const SMDS_EdgePosition* pEPos =
5757         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5758       double aT = pEPos->GetUParameter();
5759       aPrms.push_back( aT );
5760     }
5761     //Extrusion_Error err =
5762     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5763   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5764     list< SMESH_subMesh* > LSM;
5765     TopTools_SequenceOfShape Edges;
5766     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5767     while(itSM->more()) {
5768       SMESH_subMesh* SM = itSM->next();
5769       LSM.push_back(SM);
5770       const TopoDS_Shape& aS = SM->GetSubShape();
5771       Edges.Append(aS);
5772     }
5773     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5774     int startNid = theN1->GetID();
5775     TColStd_MapOfInteger UsedNums;
5776
5777     int NbEdges = Edges.Length();
5778     int i = 1;
5779     for(; i<=NbEdges; i++) {
5780       int k = 0;
5781       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5782       for(; itLSM!=LSM.end(); itLSM++) {
5783         k++;
5784         if(UsedNums.Contains(k)) continue;
5785         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5786         SMESH_subMesh* locTrack = *itLSM;
5787         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5788         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5789         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5790         const SMDS_MeshNode* aN1 = aItN->next();
5791         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5792         const SMDS_MeshNode* aN2 = aItN->next();
5793         // starting node must be aN1 or aN2
5794         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5795         // 2. Collect parameters on the track edge
5796         aPrms.clear();
5797         aItN = locMeshDS->GetNodes();
5798         while ( aItN->more() ) {
5799           const SMDS_MeshNode* pNode = aItN->next();
5800           const SMDS_EdgePosition* pEPos =
5801             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5802           double aT = pEPos->GetUParameter();
5803           aPrms.push_back( aT );
5804         }
5805         list<SMESH_MeshEditor_PathPoint> LPP;
5806         //Extrusion_Error err =
5807         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5808         LLPPs.push_back(LPP);
5809         UsedNums.Add(k);
5810         // update startN for search following egde
5811         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5812         else startNid = aN1->GetID();
5813         break;
5814       }
5815     }
5816     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5817     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5818     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5819     for(; itPP!=firstList.end(); itPP++) {
5820       fullList.push_back( *itPP );
5821     }
5822     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5823     fullList.pop_back();
5824     itLLPP++;
5825     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5826       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5827       itPP = currList.begin();
5828       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5829       gp_Dir D1 = PP1.Tangent();
5830       gp_Dir D2 = PP2.Tangent();
5831       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5832                            (D1.Z()+D2.Z())/2 ) );
5833       PP1.SetTangent(Dnew);
5834       fullList.push_back(PP1);
5835       itPP++;
5836       for(; itPP!=firstList.end(); itPP++) {
5837         fullList.push_back( *itPP );
5838       }
5839       PP1 = fullList.back();
5840       fullList.pop_back();
5841     }
5842     // if wire not closed
5843     fullList.push_back(PP1);
5844     // else ???
5845   }
5846   else {
5847     return EXTR_BAD_PATH_SHAPE;
5848   }
5849
5850   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5851                           theHasRefPoint, theRefPoint, theMakeGroups);
5852 }
5853
5854
5855 //=======================================================================
5856 //function : ExtrusionAlongTrack
5857 //purpose  :
5858 //=======================================================================
5859 SMESH_MeshEditor::Extrusion_Error
5860 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet &   theElements,
5861                                        SMESH_Mesh*          theTrack,
5862                                        const SMDS_MeshNode* theN1,
5863                                        const bool           theHasAngles,
5864                                        list<double>&        theAngles,
5865                                        const bool           theLinearVariation,
5866                                        const bool           theHasRefPoint,
5867                                        const gp_Pnt&        theRefPoint,
5868                                        const bool           theMakeGroups)
5869 {
5870   myLastCreatedElems.Clear();
5871   myLastCreatedNodes.Clear();
5872
5873   int aNbE;
5874   std::list<double> aPrms;
5875   TIDSortedElemSet::iterator itElem;
5876
5877   gp_XYZ aGC;
5878   TopoDS_Edge aTrackEdge;
5879   TopoDS_Vertex aV1, aV2;
5880
5881   SMDS_ElemIteratorPtr aItE;
5882   SMDS_NodeIteratorPtr aItN;
5883   SMDSAbs_ElementType aTypeE;
5884
5885   TNodeOfNodeListMap mapNewNodes;
5886
5887   // 1. Check data
5888   aNbE = theElements.size();
5889   // nothing to do
5890   if ( !aNbE )
5891     return EXTR_NO_ELEMENTS;
5892
5893   // 1.1 Track Pattern
5894   ASSERT( theTrack );
5895
5896   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5897
5898   aItE = pMeshDS->elementsIterator();
5899   while ( aItE->more() ) {
5900     const SMDS_MeshElement* pE = aItE->next();
5901     aTypeE = pE->GetType();
5902     // Pattern must contain links only
5903     if ( aTypeE != SMDSAbs_Edge )
5904       return EXTR_PATH_NOT_EDGE;
5905   }
5906
5907   list<SMESH_MeshEditor_PathPoint> fullList;
5908
5909   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5910
5911   if ( !theTrack->HasShapeToMesh() ) {
5912     //Mesh without shape
5913     const SMDS_MeshNode* currentNode = NULL;
5914     const SMDS_MeshNode* prevNode = theN1;
5915     std::vector<const SMDS_MeshNode*> aNodesList;
5916     aNodesList.push_back(theN1);
5917     int nbEdges = 0, conn=0;
5918     const SMDS_MeshElement* prevElem = NULL;
5919     const SMDS_MeshElement* currentElem = NULL;
5920     int totalNbEdges = theTrack->NbEdges();
5921     SMDS_ElemIteratorPtr nIt;
5922
5923     //check start node
5924     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5925       return EXTR_BAD_STARTING_NODE;
5926     }
5927
5928     conn = nbEdgeConnectivity(theN1);
5929     if(conn > 2)
5930       return EXTR_PATH_NOT_EDGE;
5931
5932     aItE = theN1->GetInverseElementIterator();
5933     prevElem = aItE->next();
5934     currentElem = prevElem;
5935     //Get all nodes
5936     if(totalNbEdges == 1 ) {
5937       nIt = currentElem->nodesIterator();
5938       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5939       if(currentNode == prevNode)
5940         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5941       aNodesList.push_back(currentNode);
5942     } else {
5943       nIt = currentElem->nodesIterator();
5944       while( nIt->more() ) {
5945         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5946         if(currentNode == prevNode)
5947           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5948         aNodesList.push_back(currentNode);
5949
5950         //case of the closed mesh
5951         if(currentNode == theN1) {
5952           nbEdges++;
5953           break;
5954         }
5955
5956         conn = nbEdgeConnectivity(currentNode);
5957         if(conn > 2) {
5958           return EXTR_PATH_NOT_EDGE;
5959         }else if( conn == 1 && nbEdges > 0 ) {
5960           //End of the path
5961           nbEdges++;
5962           break;
5963         }else {
5964           prevNode = currentNode;
5965           aItE = currentNode->GetInverseElementIterator();
5966           currentElem = aItE->next();
5967           if( currentElem  == prevElem)
5968             currentElem = aItE->next();
5969           nIt = currentElem->nodesIterator();
5970           prevElem = currentElem;
5971           nbEdges++;
5972         }
5973       }
5974     }
5975
5976     if(nbEdges != totalNbEdges)
5977       return EXTR_PATH_NOT_EDGE;
5978
5979     TopTools_SequenceOfShape Edges;
5980     double x1,x2,y1,y2,z1,z2;
5981     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5982     int startNid = theN1->GetID();
5983     for(int i = 1; i < aNodesList.size(); i++) {
5984       x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5985       y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5986       z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5987       TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5988       list<SMESH_MeshEditor_PathPoint> LPP;
5989       aPrms.clear();
5990       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5991       LLPPs.push_back(LPP);
5992       if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5993       else startNid = aNodesList[i-1]->GetID();
5994
5995     }
5996
5997     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5998     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5999     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6000     for(; itPP!=firstList.end(); itPP++) {
6001       fullList.push_back( *itPP );
6002     }
6003
6004     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6005     SMESH_MeshEditor_PathPoint PP2;
6006     fullList.pop_back();
6007     itLLPP++;
6008     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6009       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6010       itPP = currList.begin();
6011       PP2 = currList.front();
6012       gp_Dir D1 = PP1.Tangent();
6013       gp_Dir D2 = PP2.Tangent();
6014       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6015                            (D1.Z()+D2.Z())/2 ) );
6016       PP1.SetTangent(Dnew);
6017       fullList.push_back(PP1);
6018       itPP++;
6019       for(; itPP!=currList.end(); itPP++) {
6020         fullList.push_back( *itPP );
6021       }
6022       PP1 = fullList.back();
6023       fullList.pop_back();
6024     }
6025     fullList.push_back(PP1);
6026
6027   } // Sub-shape for the Pattern must be an Edge or Wire
6028   else if( aS.ShapeType() == TopAbs_EDGE ) {
6029     aTrackEdge = TopoDS::Edge( aS );
6030     // the Edge must not be degenerated
6031     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6032       return EXTR_BAD_PATH_SHAPE;
6033     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6034     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6035     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6036     // starting node must be aN1 or aN2
6037     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6038       return EXTR_BAD_STARTING_NODE;
6039     aItN = pMeshDS->nodesIterator();
6040     while ( aItN->more() ) {
6041       const SMDS_MeshNode* pNode = aItN->next();
6042       if( pNode==aN1 || pNode==aN2 ) continue;
6043       const SMDS_EdgePosition* pEPos =
6044         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6045       double aT = pEPos->GetUParameter();
6046       aPrms.push_back( aT );
6047     }
6048     //Extrusion_Error err =
6049     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6050   }
6051   else if( aS.ShapeType() == TopAbs_WIRE ) {
6052     list< SMESH_subMesh* > LSM;
6053     TopTools_SequenceOfShape Edges;
6054     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6055     for(; eExp.More(); eExp.Next()) {
6056       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6057       if( SMESH_Algo::isDegenerated(E) ) continue;
6058       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6059       if(SM) {
6060         LSM.push_back(SM);
6061         Edges.Append(E);
6062       }
6063     }
6064     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6065     TopoDS_Vertex aVprev;
6066     TColStd_MapOfInteger UsedNums;
6067     int NbEdges = Edges.Length();
6068     int i = 1;
6069     for(; i<=NbEdges; i++) {
6070       int k = 0;
6071       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6072       for(; itLSM!=LSM.end(); itLSM++) {
6073         k++;
6074         if(UsedNums.Contains(k)) continue;
6075         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6076         SMESH_subMesh* locTrack = *itLSM;
6077         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6078         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6079         bool aN1isOK = false, aN2isOK = false;
6080         if ( aVprev.IsNull() ) {
6081           // if previous vertex is not yet defined, it means that we in the beginning of wire
6082           // and we have to find initial vertex corresponding to starting node theN1
6083           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6084           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6085           // starting node must be aN1 or aN2
6086           aN1isOK = ( aN1 && aN1 == theN1 );
6087           aN2isOK = ( aN2 && aN2 == theN1 );
6088         }
6089         else {
6090           // we have specified ending vertex of the previous edge on the previous iteration
6091           // and we have just to check that it corresponds to any vertex in current segment
6092           aN1isOK = aVprev.IsSame( aV1 );
6093           aN2isOK = aVprev.IsSame( aV2 );
6094         }
6095         if ( !aN1isOK && !aN2isOK ) continue;
6096         // 2. Collect parameters on the track edge
6097         aPrms.clear();
6098         aItN = locMeshDS->GetNodes();
6099         while ( aItN->more() ) {
6100           const SMDS_MeshNode*     pNode = aItN->next();
6101           const SMDS_EdgePosition* pEPos =
6102             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6103           double aT = pEPos->GetUParameter();
6104           aPrms.push_back( aT );
6105         }
6106         list<SMESH_MeshEditor_PathPoint> LPP;
6107         //Extrusion_Error err =
6108         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6109         LLPPs.push_back(LPP);
6110         UsedNums.Add(k);
6111         // update startN for search following egde
6112         if ( aN1isOK ) aVprev = aV2;
6113         else           aVprev = aV1;
6114         break;
6115       }
6116     }
6117     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6118     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6119     fullList.splice( fullList.end(), firstList );
6120
6121     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6122     fullList.pop_back();
6123     itLLPP++;
6124     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6125       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6126       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6127       gp_Dir D1 = PP1.Tangent();
6128       gp_Dir D2 = PP2.Tangent();
6129       gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6130       PP1.SetTangent(Dnew);
6131       fullList.push_back(PP1);
6132       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6133       PP1 = fullList.back();
6134       fullList.pop_back();
6135     }
6136     // if wire not closed
6137     fullList.push_back(PP1);
6138     // else ???
6139   }
6140   else {
6141     return EXTR_BAD_PATH_SHAPE;
6142   }
6143
6144   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6145                           theHasRefPoint, theRefPoint, theMakeGroups);
6146 }
6147
6148
6149 //=======================================================================
6150 //function : MakeEdgePathPoints
6151 //purpose  : auxilary for ExtrusionAlongTrack
6152 //=======================================================================
6153 SMESH_MeshEditor::Extrusion_Error
6154 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6155                                      const TopoDS_Edge&                aTrackEdge,
6156                                      bool                              FirstIsStart,
6157                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6158 {
6159   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6160   aTolVec=1.e-7;
6161   aTolVec2=aTolVec*aTolVec;
6162   double aT1, aT2;
6163   TopoDS_Vertex aV1, aV2;
6164   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6165   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6166   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6167   // 2. Collect parameters on the track edge
6168   aPrms.push_front( aT1 );
6169   aPrms.push_back( aT2 );
6170   // sort parameters
6171   aPrms.sort();
6172   if( FirstIsStart ) {
6173     if ( aT1 > aT2 ) {
6174       aPrms.reverse();
6175     }
6176   }
6177   else {
6178     if ( aT2 > aT1 ) {
6179       aPrms.reverse();
6180     }
6181   }
6182   // 3. Path Points
6183   SMESH_MeshEditor_PathPoint aPP;
6184   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6185   std::list<double>::iterator aItD = aPrms.begin();
6186   for(; aItD != aPrms.end(); ++aItD) {
6187     double aT = *aItD;
6188     gp_Pnt aP3D;
6189     gp_Vec aVec;
6190     aC3D->D1( aT, aP3D, aVec );
6191     aL2 = aVec.SquareMagnitude();
6192     if ( aL2 < aTolVec2 )
6193       return EXTR_CANT_GET_TANGENT;
6194     gp_Dir aTgt( aVec );
6195     aPP.SetPnt( aP3D );
6196     aPP.SetTangent( aTgt );
6197     aPP.SetParameter( aT );
6198     LPP.push_back(aPP);
6199   }
6200   return EXTR_OK;
6201 }
6202
6203
6204 //=======================================================================
6205 //function : MakeExtrElements
6206 //purpose  : auxilary for ExtrusionAlongTrack
6207 //=======================================================================
6208 SMESH_MeshEditor::Extrusion_Error
6209 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet&                 theElements,
6210                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6211                                    const bool                        theHasAngles,
6212                                    list<double>&                     theAngles,
6213                                    const bool                        theLinearVariation,
6214                                    const bool                        theHasRefPoint,
6215                                    const gp_Pnt&                     theRefPoint,
6216                                    const bool                        theMakeGroups)
6217 {
6218   const int aNbTP = fullList.size();
6219   // Angles
6220   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6221     LinearAngleVariation(aNbTP-1, theAngles);
6222   // fill vector of path points with angles
6223   vector<SMESH_MeshEditor_PathPoint> aPPs;
6224   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6225   list<double>::iterator                 itAngles = theAngles.begin();
6226   aPPs.push_back( *itPP++ );
6227   for( ; itPP != fullList.end(); itPP++) {
6228     aPPs.push_back( *itPP );
6229     if ( theHasAngles && itAngles != theAngles.end() )
6230       aPPs.back().SetAngle( *itAngles++ );
6231   }
6232
6233   TNodeOfNodeListMap   mapNewNodes;
6234   TElemOfVecOfNnlmiMap mapElemNewNodes;
6235   TTElemOfElemListMap  newElemsMap;
6236   TIDSortedElemSet::iterator itElem;
6237   // source elements for each generated one
6238   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6239
6240   // 3. Center of rotation aV0
6241   gp_Pnt aV0 = theRefPoint;
6242   if ( !theHasRefPoint )
6243   {
6244     gp_XYZ aGC( 0.,0.,0. );
6245     TIDSortedElemSet newNodes;
6246
6247     itElem = theElements.begin();
6248     for ( ; itElem != theElements.end(); itElem++ ) {
6249       const SMDS_MeshElement* elem = *itElem;
6250
6251       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6252       while ( itN->more() ) {
6253         const SMDS_MeshElement* node = itN->next();
6254         if ( newNodes.insert( node ).second )
6255           aGC += SMESH_TNodeXYZ( node );
6256       }
6257     }
6258     aGC /= newNodes.size();
6259     aV0.SetXYZ( aGC );
6260   } // if (!theHasRefPoint) {
6261
6262   // 4. Processing the elements
6263   SMESHDS_Mesh* aMesh = GetMeshDS();
6264
6265   for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6266     // check element type
6267     const SMDS_MeshElement* elem = *itElem;
6268     SMDSAbs_ElementType   aTypeE = elem->GetType();
6269     if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
6270       continue;
6271
6272     vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6273     newNodesItVec.reserve( elem->NbNodes() );
6274
6275     // loop on elem nodes
6276     int nodeIndex = -1;
6277     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6278     while ( itN->more() )
6279     {
6280       ++nodeIndex;
6281       // check if a node has been already processed
6282       const SMDS_MeshNode* node =
6283         static_cast<const SMDS_MeshNode*>( itN->next() );
6284       TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6285       if ( nIt == mapNewNodes.end() ) {
6286         nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6287         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6288
6289         // make new nodes
6290         Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6291         gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6292         gp_Ax1 anAx1, anAxT1T0;
6293         gp_Dir aDT1x, aDT0x, aDT1T0;
6294
6295         aTolAng=1.e-4;
6296
6297         aV0x = aV0;
6298         aPN0 = SMESH_TNodeXYZ( node );
6299
6300         const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6301         aP0x = aPP0.Pnt();
6302         aDT0x= aPP0.Tangent();
6303         //cout<<"j = 0   PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6304
6305         for ( int j = 1; j < aNbTP; ++j ) {
6306           const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6307           aP1x     = aPP1.Pnt();
6308           aDT1x    = aPP1.Tangent();
6309           aAngle1x = aPP1.Angle();
6310
6311           gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6312           // Translation
6313           gp_Vec aV01x( aP0x, aP1x );
6314           aTrsf.SetTranslation( aV01x );
6315
6316           // traslated point
6317           aV1x = aV0x.Transformed( aTrsf );
6318           aPN1 = aPN0.Transformed( aTrsf );
6319
6320           // rotation 1 [ T1,T0 ]
6321           aAngleT1T0=-aDT1x.Angle( aDT0x );
6322           if (fabs(aAngleT1T0) > aTolAng) {
6323             aDT1T0=aDT1x^aDT0x;
6324             anAxT1T0.SetLocation( aV1x );
6325             anAxT1T0.SetDirection( aDT1T0 );
6326             aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6327
6328             aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6329           }
6330
6331           // rotation 2
6332           if ( theHasAngles ) {
6333             anAx1.SetLocation( aV1x );
6334             anAx1.SetDirection( aDT1x );
6335             aTrsfRot.SetRotation( anAx1, aAngle1x );
6336
6337             aPN1 = aPN1.Transformed( aTrsfRot );
6338           }
6339
6340           // make new node
6341           //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6342           if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6343             // create additional node
6344             double x = ( aPN1.X() + aPN0.X() )/2.;
6345             double y = ( aPN1.Y() + aPN0.Y() )/2.;
6346             double z = ( aPN1.Z() + aPN0.Z() )/2.;
6347             const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6348             myLastCreatedNodes.Append(newNode);
6349             srcNodes.Append( node );
6350             listNewNodes.push_back( newNode );
6351           }
6352           const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6353           myLastCreatedNodes.Append(newNode);
6354           srcNodes.Append( node );
6355           listNewNodes.push_back( newNode );
6356
6357           aPN0 = aPN1;
6358           aP0x = aP1x;
6359           aV0x = aV1x;
6360           aDT0x = aDT1x;
6361         }
6362       }
6363
6364       else {
6365         // if current elem is quadratic and current node is not medium
6366         // we have to check - may be it is needed to insert additional nodes
6367         if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6368           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6369           if(listNewNodes.size()==aNbTP-1) {
6370             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6371             gp_XYZ P(node->X(), node->Y(), node->Z());
6372             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6373             int i;
6374             for(i=0; i<aNbTP-1; i++) {
6375               const SMDS_MeshNode* N = *it;
6376               double x = ( N->X() + P.X() )/2.;
6377               double y = ( N->Y() + P.Y() )/2.;
6378               double z = ( N->Z() + P.Z() )/2.;
6379               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6380               srcNodes.Append( node );
6381               myLastCreatedNodes.Append(newN);
6382               aNodes[2*i] = newN;
6383               aNodes[2*i+1] = N;
6384               P = gp_XYZ(N->X(),N->Y(),N->Z());
6385             }
6386             listNewNodes.clear();
6387             for(i=0; i<2*(aNbTP-1); i++) {
6388               listNewNodes.push_back(aNodes[i]);
6389             }
6390           }
6391         }
6392       }
6393
6394       newNodesItVec.push_back( nIt );
6395     }
6396     // make new elements
6397     //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6398     //              newNodesItVec[0]->second.size(), myLastCreatedElems );
6399     sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6400   }
6401
6402   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6403
6404   if ( theMakeGroups )
6405     generateGroups( srcNodes, srcElems, "extruded");
6406
6407   return EXTR_OK;
6408 }
6409
6410
6411 //=======================================================================
6412 //function : LinearAngleVariation
6413 //purpose  : auxilary for ExtrusionAlongTrack
6414 //=======================================================================
6415 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6416                                             list<double>& Angles)
6417 {
6418   int nbAngles = Angles.size();
6419   if( nbSteps > nbAngles ) {
6420     vector<double> theAngles(nbAngles);
6421     list<double>::iterator it = Angles.begin();
6422     int i = -1;
6423     for(; it!=Angles.end(); it++) {
6424       i++;
6425       theAngles[i] = (*it);
6426     }
6427     list<double> res;
6428     double rAn2St = double( nbAngles ) / double( nbSteps );
6429     double angPrev = 0, angle;
6430     for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6431       double angCur = rAn2St * ( iSt+1 );
6432       double angCurFloor  = floor( angCur );
6433       double angPrevFloor = floor( angPrev );
6434       if ( angPrevFloor == angCurFloor )
6435         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6436       else {
6437         int iP = int( angPrevFloor );
6438         double angPrevCeil = ceil(angPrev);
6439         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6440
6441         int iC = int( angCurFloor );
6442         if ( iC < nbAngles )
6443           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6444
6445         iP = int( angPrevCeil );
6446         while ( iC-- > iP )
6447           angle += theAngles[ iC ];
6448       }
6449       res.push_back(angle);
6450       angPrev = angCur;
6451     }
6452     Angles.clear();
6453     it = res.begin();
6454     for(; it!=res.end(); it++)
6455       Angles.push_back( *it );
6456   }
6457 }
6458
6459
6460 //================================================================================
6461 /*!
6462  * \brief Move or copy theElements applying theTrsf to their nodes
6463  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6464  *  \param theTrsf - transformation to apply
6465  *  \param theCopy - if true, create translated copies of theElems
6466  *  \param theMakeGroups - if true and theCopy, create translated groups
6467  *  \param theTargetMesh - mesh to copy translated elements into
6468  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6469  */
6470 //================================================================================
6471
6472 SMESH_MeshEditor::PGroupIDs
6473 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6474                              const gp_Trsf&     theTrsf,
6475                              const bool         theCopy,
6476                              const bool         theMakeGroups,
6477                              SMESH_Mesh*        theTargetMesh)
6478 {
6479   myLastCreatedElems.Clear();
6480   myLastCreatedNodes.Clear();
6481
6482   bool needReverse = false;
6483   string groupPostfix;
6484   switch ( theTrsf.Form() ) {
6485   case gp_PntMirror:
6486     MESSAGE("gp_PntMirror");
6487     needReverse = true;
6488     groupPostfix = "mirrored";
6489     break;
6490   case gp_Ax1Mirror:
6491     MESSAGE("gp_Ax1Mirror");
6492     groupPostfix = "mirrored";
6493     break;
6494   case gp_Ax2Mirror:
6495     MESSAGE("gp_Ax2Mirror");
6496     needReverse = true;
6497     groupPostfix = "mirrored";
6498     break;
6499   case gp_Rotation:
6500     MESSAGE("gp_Rotation");
6501     groupPostfix = "rotated";
6502     break;
6503   case gp_Translation:
6504     MESSAGE("gp_Translation");
6505     groupPostfix = "translated";
6506     break;
6507   case gp_Scale:
6508     MESSAGE("gp_Scale");
6509     groupPostfix = "scaled";
6510     break;
6511   case gp_CompoundTrsf: // different scale by axis
6512     MESSAGE("gp_CompoundTrsf");
6513     groupPostfix = "scaled";
6514     break;
6515   default:
6516     MESSAGE("default");
6517     needReverse = false;
6518     groupPostfix = "transformed";
6519   }
6520
6521   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6522   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6523   SMESHDS_Mesh* aMesh    = GetMeshDS();
6524
6525
6526   // map old node to new one
6527   TNodeNodeMap nodeMap;
6528
6529   // elements sharing moved nodes; those of them which have all
6530   // nodes mirrored but are not in theElems are to be reversed
6531   TIDSortedElemSet inverseElemSet;
6532
6533   // source elements for each generated one
6534   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6535
6536   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6537   TIDSortedElemSet orphanNode;
6538
6539   if ( theElems.empty() ) // transform the whole mesh
6540   {
6541     // add all elements
6542     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6543     while ( eIt->more() ) theElems.insert( eIt->next() );
6544     // add orphan nodes
6545     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6546     while ( nIt->more() )
6547     {
6548       const SMDS_MeshNode* node = nIt->next();
6549       if ( node->NbInverseElements() == 0)
6550         orphanNode.insert( node );
6551     }
6552   }
6553
6554   // loop on elements to transform nodes : first orphan nodes then elems
6555   TIDSortedElemSet::iterator itElem;
6556   TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6557   for (int i=0; i<2; i++)
6558   for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6559     const SMDS_MeshElement* elem = *itElem;
6560     if ( !elem )
6561       continue;
6562
6563     // loop on elem nodes
6564     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6565     while ( itN->more() ) {
6566
6567       const SMDS_MeshNode* node = cast2Node( itN->next() );
6568       // check if a node has been already transformed
6569       pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6570         nodeMap.insert( make_pair ( node, node ));
6571       if ( !n2n_isnew.second )
6572         continue;
6573
6574       double coord[3];
6575       coord[0] = node->X();
6576       coord[1] = node->Y();
6577       coord[2] = node->Z();
6578       theTrsf.Transforms( coord[0], coord[1], coord[2] );
6579       if ( theTargetMesh ) {
6580         const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6581         n2n_isnew.first->second = newNode;
6582         myLastCreatedNodes.Append(newNode);
6583         srcNodes.Append( node );
6584       }
6585       else if ( theCopy ) {
6586         const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6587         n2n_isnew.first->second = newNode;
6588         myLastCreatedNodes.Append(newNode);
6589         srcNodes.Append( node );
6590       }
6591       else {
6592         aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6593         // node position on shape becomes invalid
6594         const_cast< SMDS_MeshNode* > ( node )->SetPosition
6595           ( SMDS_SpacePosition::originSpacePosition() );
6596       }
6597
6598       // keep inverse elements
6599       if ( !theCopy && !theTargetMesh && needReverse ) {
6600         SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6601         while ( invElemIt->more() ) {
6602           const SMDS_MeshElement* iel = invElemIt->next();
6603           inverseElemSet.insert( iel );
6604         }
6605       }
6606     }
6607   }
6608
6609   // either create new elements or reverse mirrored ones
6610   if ( !theCopy && !needReverse && !theTargetMesh )
6611     return PGroupIDs();
6612
6613   TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6614   for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6615     theElems.insert( *invElemIt );
6616
6617   // Replicate or reverse elements
6618
6619   std::vector<int> iForw;
6620   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6621   {
6622     const SMDS_MeshElement* elem = *itElem;
6623     if ( !elem ) continue;
6624
6625     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6626     int                  nbNodes  = elem->NbNodes();
6627     if ( geomType == SMDSGeom_NONE ) continue; // node
6628
6629     switch ( geomType ) {
6630
6631     case SMDSGeom_POLYGON:  // ---------------------- polygon
6632       {
6633         vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6634         int iNode = 0;
6635         SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6636         while (itN->more()) {
6637           const SMDS_MeshNode* node =
6638             static_cast<const SMDS_MeshNode*>(itN->next());
6639           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6640           if (nodeMapIt == nodeMap.end())
6641             break; // not all nodes transformed
6642           if (needReverse) {
6643             // reverse mirrored faces and volumes
6644             poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6645           } else {
6646             poly_nodes[iNode] = (*nodeMapIt).second;
6647           }
6648           iNode++;
6649         }
6650         if ( iNode != nbNodes )
6651           continue; // not all nodes transformed
6652
6653         if ( theTargetMesh ) {
6654           myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6655           srcElems.Append( elem );
6656         }
6657         else if ( theCopy ) {
6658           myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6659           srcElems.Append( elem );
6660         }
6661         else {
6662           aMesh->ChangePolygonNodes(elem, poly_nodes);
6663         }
6664       }
6665       break;
6666
6667     case SMDSGeom_POLYHEDRA:  // ------------------ polyhedral volume
6668       {
6669         const SMDS_VtkVolume* aPolyedre =
6670           dynamic_cast<const SMDS_VtkVolume*>( elem );
6671         if (!aPolyedre) {
6672           MESSAGE("Warning: bad volumic element");
6673           continue;
6674         }
6675
6676         vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6677         vector<int> quantities; quantities.reserve( nbNodes );
6678
6679         bool allTransformed = true;
6680         int nbFaces = aPolyedre->NbFaces();
6681         for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6682           int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6683           for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6684             const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6685             TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6686             if (nodeMapIt == nodeMap.end()) {
6687               allTransformed = false; // not all nodes transformed
6688             } else {
6689               poly_nodes.push_back((*nodeMapIt).second);
6690             }
6691             if ( needReverse && allTransformed )
6692               std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6693           }
6694           quantities.push_back(nbFaceNodes);
6695         }
6696         if ( !allTransformed )
6697           continue; // not all nodes transformed
6698
6699         if ( theTargetMesh ) {
6700           myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6701           srcElems.Append( elem );
6702         }
6703         else if ( theCopy ) {
6704           myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6705           srcElems.Append( elem );
6706         }
6707         else {
6708           aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6709         }
6710       }
6711       break;
6712
6713     case SMDSGeom_BALL: // -------------------- Ball
6714       {
6715         if ( !theCopy && !theTargetMesh ) continue;
6716
6717         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6718         if (nodeMapIt == nodeMap.end())
6719           continue; // not all nodes transformed
6720
6721         double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6722         if ( theTargetMesh ) {
6723           myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6724           srcElems.Append( elem );
6725         }
6726         else {
6727           myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6728           srcElems.Append( elem );
6729         }
6730       }
6731       break;
6732
6733     default: // ----------------------- Regular elements
6734
6735       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6736       const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6737       const std::vector<int>& i = needReverse ? iRev : iForw;
6738
6739       // find transformed nodes
6740       vector<const SMDS_MeshNode*> nodes(nbNodes);
6741       int iNode = 0;
6742       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6743       while ( itN->more() ) {
6744         const SMDS_MeshNode* node =
6745           static_cast<const SMDS_MeshNode*>( itN->next() );
6746         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6747         if ( nodeMapIt == nodeMap.end() )
6748           break; // not all nodes transformed
6749         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6750       }
6751       if ( iNode != nbNodes )
6752         continue; // not all nodes transformed
6753
6754       if ( theTargetMesh ) {
6755         if ( SMDS_MeshElement* copy =
6756              targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6757           myLastCreatedElems.Append( copy );
6758           srcElems.Append( elem );
6759         }
6760       }
6761       else if ( theCopy ) {
6762         if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6763           srcElems.Append( elem );
6764       }
6765       else {
6766         // reverse element as it was reversed by transformation
6767         if ( nbNodes > 2 )
6768           aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6769       }
6770     } // switch ( geomType )
6771
6772   } // loop on elements
6773
6774   PGroupIDs newGroupIDs;
6775
6776   if ( ( theMakeGroups && theCopy ) ||
6777        ( theMakeGroups && theTargetMesh ) )
6778     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6779
6780   return newGroupIDs;
6781 }
6782
6783 //=======================================================================
6784 /*!
6785  * \brief Create groups of elements made during transformation
6786  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6787  *  \param elemGens - elements making corresponding myLastCreatedElems
6788  *  \param postfix - to append to names of new groups
6789  *  \param targetMesh - mesh to create groups in
6790  *  \param topPresent - is there "top" elements that are created by sweeping
6791  */
6792 //=======================================================================
6793
6794 SMESH_MeshEditor::PGroupIDs
6795 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6796                                  const SMESH_SequenceOfElemPtr& elemGens,
6797                                  const std::string&             postfix,
6798                                  SMESH_Mesh*                    targetMesh,
6799                                  const bool                     topPresent)
6800 {
6801   PGroupIDs newGroupIDs( new list<int> );
6802   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6803
6804   // Sort existing groups by types and collect their names
6805
6806   // containers to store an old group and generated new ones;
6807   // 1st new group is for result elems of different type than a source one;
6808   // 2nd new group is for same type result elems ("top" group at extrusion)
6809   using boost::tuple;
6810   using boost::make_tuple;
6811   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6812   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6813   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6814   // group names
6815   set< string > groupNames;
6816
6817   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6818   if ( !groupIt->more() ) return newGroupIDs;
6819
6820   int newGroupID = mesh->GetGroupIds().back()+1;
6821   while ( groupIt->more() )
6822   {
6823     SMESH_Group * group = groupIt->next();
6824     if ( !group ) continue;
6825     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6826     if ( !groupDS || groupDS->IsEmpty() ) continue;
6827     groupNames.insert    ( group->GetName() );
6828     groupDS->SetStoreName( group->GetName() );
6829     const SMDSAbs_ElementType type = groupDS->GetType();
6830     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6831     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6832     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6833     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6834   }
6835
6836   // Loop on nodes and elements to add them in new groups
6837
6838   vector< const SMDS_MeshElement* > resultElems;
6839   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6840   {
6841     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6842     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6843     if ( gens.Length() != elems.Length() )
6844       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6845
6846     // loop on created elements
6847     for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6848     {
6849       const SMDS_MeshElement* sourceElem = gens( iElem );
6850       if ( !sourceElem ) {
6851         MESSAGE("generateGroups(): NULL source element");
6852         continue;
6853       }
6854       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6855       if ( groupsOldNew.empty() ) { // no groups of this type at all
6856         while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6857           ++iElem; // skip all elements made by sourceElem
6858         continue;
6859       }
6860       // collect all elements made by the iElem-th sourceElem
6861       resultElems.clear();
6862       if ( const SMDS_MeshElement* resElem = elems( iElem ))
6863         if ( resElem != sourceElem )
6864           resultElems.push_back( resElem );
6865       while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6866         if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6867           if ( resElem != sourceElem )
6868             resultElems.push_back( resElem );
6869
6870       const SMDS_MeshElement* topElem = 0;
6871       if ( isNodes ) // there must be a top element
6872       {
6873         topElem = resultElems.back();
6874         resultElems.pop_back();
6875       }
6876       else
6877       {
6878         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6879         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6880           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6881           {
6882             topElem = *resElemIt;
6883             *resElemIt = 0; // erase *resElemIt
6884             break;
6885           }
6886       }
6887       // add resultElems to groups originted from ones the sourceElem belongs to
6888       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6889       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6890       {
6891         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6892         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6893         {
6894           // fill in a new group
6895           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6896           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6897           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6898             if ( *resElemIt )
6899               newGroup.Add( *resElemIt );
6900
6901           // fill a "top" group
6902           if ( topElem )
6903           {
6904             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6905             newTopGroup.Add( topElem );
6906          }
6907         }
6908       }
6909     } // loop on created elements
6910   }// loop on nodes and elements
6911
6912   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6913
6914   list<int> topGrouIds;
6915   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6916   {
6917     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6918     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6919                                       orderedOldNewGroups[i]->get<2>() };
6920     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6921     {
6922       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6923       if ( newGroupDS->IsEmpty() )
6924       {
6925         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6926       }
6927       else
6928       {
6929         // set group type
6930         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6931
6932         // make a name
6933         const bool isTop = ( topPresent &&
6934                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6935                              is2nd );
6936
6937         string name = oldGroupDS->GetStoreName();
6938         { // remove trailing whitespaces (issue 22599)
6939           size_t size = name.size();
6940           while ( size > 1 && isspace( name[ size-1 ]))
6941             --size;
6942           if ( size != name.size() )
6943           {
6944             name.resize( size );
6945             oldGroupDS->SetStoreName( name.c_str() );
6946           }
6947         }
6948         if ( !targetMesh ) {
6949           string suffix = ( isTop ? "top": postfix.c_str() );
6950           name += "_";
6951           name += suffix;
6952           int nb = 1;
6953           while ( !groupNames.insert( name ).second ) // name exists
6954             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6955         }
6956         else if ( isTop ) {
6957           name += "_top";
6958         }
6959         newGroupDS->SetStoreName( name.c_str() );
6960
6961         // make a SMESH_Groups
6962         mesh->AddGroup( newGroupDS );
6963         if ( isTop )
6964           topGrouIds.push_back( newGroupDS->GetID() );
6965         else
6966           newGroupIDs->push_back( newGroupDS->GetID() );
6967       }
6968     }
6969   }
6970   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6971
6972   return newGroupIDs;
6973 }
6974
6975 //================================================================================
6976 /*!
6977  * \brief Return list of group of nodes close to each other within theTolerance
6978  *        Search among theNodes or in the whole mesh if theNodes is empty using
6979  *        an Octree algorithm
6980  */
6981 //================================================================================
6982
6983 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
6984                                             const double         theTolerance,
6985                                             TListOfListOfNodes & theGroupsOfNodes)
6986 {
6987   myLastCreatedElems.Clear();
6988   myLastCreatedNodes.Clear();
6989
6990   if ( theNodes.empty() )
6991   { // get all nodes in the mesh
6992     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6993     while ( nIt->more() )
6994       theNodes.insert( theNodes.end(),nIt->next());
6995   }
6996
6997   SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6998 }
6999
7000 //=======================================================================
7001 //function : SimplifyFace
7002 //purpose  :
7003 //=======================================================================
7004
7005 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7006                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7007                                     vector<int>&                         quantities) const
7008 {
7009   int nbNodes = faceNodes.size();
7010
7011   if (nbNodes < 3)
7012     return 0;
7013
7014   set<const SMDS_MeshNode*> nodeSet;
7015
7016   // get simple seq of nodes
7017   //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7018   vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7019   int iSimple = 0, nbUnique = 0;
7020
7021   simpleNodes[iSimple++] = faceNodes[0];
7022   nbUnique++;
7023   for (int iCur = 1; iCur < nbNodes; iCur++) {
7024     if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7025       simpleNodes[iSimple++] = faceNodes[iCur];
7026       if (nodeSet.insert( faceNodes[iCur] ).second)
7027         nbUnique++;
7028     }
7029   }
7030   int nbSimple = iSimple;
7031   if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7032     nbSimple--;
7033     iSimple--;
7034   }
7035
7036   if (nbUnique < 3)
7037     return 0;
7038
7039   // separate loops
7040   int nbNew = 0;
7041   bool foundLoop = (nbSimple > nbUnique);
7042   while (foundLoop) {
7043     foundLoop = false;
7044     set<const SMDS_MeshNode*> loopSet;
7045     for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7046       const SMDS_MeshNode* n = simpleNodes[iSimple];
7047       if (!loopSet.insert( n ).second) {
7048         foundLoop = true;
7049
7050         // separate loop
7051         int iC = 0, curLast = iSimple;
7052         for (; iC < curLast; iC++) {
7053           if (simpleNodes[iC] == n) break;
7054         }
7055         int loopLen = curLast - iC;
7056         if (loopLen > 2) {
7057           // create sub-element
7058           nbNew++;
7059           quantities.push_back(loopLen);
7060           for (; iC < curLast; iC++) {
7061             poly_nodes.push_back(simpleNodes[iC]);
7062           }
7063         }
7064         // shift the rest nodes (place from the first loop position)
7065         for (iC = curLast + 1; iC < nbSimple; iC++) {
7066           simpleNodes[iC - loopLen] = simpleNodes[iC];
7067         }
7068         nbSimple -= loopLen;
7069         iSimple -= loopLen;
7070       }
7071     } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7072   } // while (foundLoop)
7073
7074   if (iSimple > 2) {
7075     nbNew++;
7076     quantities.push_back(iSimple);
7077     for (int i = 0; i < iSimple; i++)
7078       poly_nodes.push_back(simpleNodes[i]);
7079   }
7080
7081   return nbNew;
7082 }
7083
7084 //=======================================================================
7085 //function : MergeNodes
7086 //purpose  : In each group, the cdr of nodes are substituted by the first one
7087 //           in all elements.
7088 //=======================================================================
7089
7090 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7091 {
7092   MESSAGE("MergeNodes");
7093   myLastCreatedElems.Clear();
7094   myLastCreatedNodes.Clear();
7095
7096   SMESHDS_Mesh* aMesh = GetMeshDS();
7097
7098   TNodeNodeMap nodeNodeMap; // node to replace - new node
7099   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7100   list< int > rmElemIds, rmNodeIds;
7101
7102   // Fill nodeNodeMap and elems
7103
7104   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7105   for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7106     list<const SMDS_MeshNode*>& nodes = *grIt;
7107     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7108     const SMDS_MeshNode* nToKeep = *nIt;
7109     //MESSAGE("node to keep " << nToKeep->GetID());
7110     for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7111       const SMDS_MeshNode* nToRemove = *nIt;
7112       nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7113       if ( nToRemove != nToKeep ) {
7114         //MESSAGE("  node to remove " << nToRemove->GetID());
7115         rmNodeIds.push_back( nToRemove->GetID() );
7116         AddToSameGroups( nToKeep, nToRemove, aMesh );
7117         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7118         // after MergeNodes() w/o creating node in place of merged ones.
7119         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7120         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7121           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7122             sm->SetIsAlwaysComputed( true );
7123       }
7124
7125       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7126       while ( invElemIt->more() ) {
7127         const SMDS_MeshElement* elem = invElemIt->next();
7128         elems.insert(elem);
7129       }
7130     }
7131   }
7132   // Change element nodes or remove an element
7133
7134   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7135   for ( ; eIt != elems.end(); eIt++ ) {
7136     const SMDS_MeshElement* elem = *eIt;
7137     //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7138     int nbNodes = elem->NbNodes();
7139     int aShapeId = FindShape( elem );
7140
7141     set<const SMDS_MeshNode*> nodeSet;
7142     vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7143     int iUnique = 0, iCur = 0, nbRepl = 0;
7144     vector<int> iRepl( nbNodes );
7145
7146     // get new seq of nodes
7147     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7148     while ( itN->more() ) {
7149       const SMDS_MeshNode* n =
7150         static_cast<const SMDS_MeshNode*>( itN->next() );
7151
7152       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7153       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7154         n = (*nnIt).second;
7155         // BUG 0020185: begin
7156         {
7157           bool stopRecur = false;
7158           set<const SMDS_MeshNode*> nodesRecur;
7159           nodesRecur.insert(n);
7160           while (!stopRecur) {
7161             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7162             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7163               n = (*nnIt_i).second;
7164               if (!nodesRecur.insert(n).second) {
7165                 // error: recursive dependancy
7166                 stopRecur = true;
7167               }
7168             }
7169             else
7170               stopRecur = true;
7171           }
7172         }
7173         // BUG 0020185: end
7174       }
7175       curNodes[ iCur ] = n;
7176       bool isUnique = nodeSet.insert( n ).second;
7177       if ( isUnique )
7178         uniqueNodes[ iUnique++ ] = n;
7179       else
7180         iRepl[ nbRepl++ ] = iCur;
7181       iCur++;
7182     }
7183
7184     // Analyse element topology after replacement
7185
7186     bool isOk = true;
7187     int nbUniqueNodes = nodeSet.size();
7188     //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7189     if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7190       // Polygons and Polyhedral volumes
7191       if (elem->IsPoly()) {
7192
7193         if (elem->GetType() == SMDSAbs_Face) {
7194           // Polygon
7195           vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7196           int inode = 0;
7197           for (; inode < nbNodes; inode++) {
7198             face_nodes[inode] = curNodes[inode];
7199           }
7200
7201           vector<const SMDS_MeshNode *> polygons_nodes;
7202           vector<int> quantities;
7203           int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7204           if (nbNew > 0) {
7205             inode = 0;
7206             for (int iface = 0; iface < nbNew; iface++) {
7207               int nbNodes = quantities[iface];
7208               vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7209               for (int ii = 0; ii < nbNodes; ii++, inode++) {
7210                 poly_nodes[ii] = polygons_nodes[inode];
7211               }
7212               SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7213               myLastCreatedElems.Append(newElem);
7214               if (aShapeId)
7215                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7216             }
7217
7218             MESSAGE("ChangeElementNodes MergeNodes Polygon");
7219             //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7220             vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7221             int quid =0;
7222             if (nbNew > 0) quid = nbNew - 1;
7223             vector<int> newquant(quantities.begin()+quid, quantities.end());
7224             const SMDS_MeshElement* newElem = 0;
7225             newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7226             myLastCreatedElems.Append(newElem);
7227             if ( aShapeId && newElem )
7228               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7229             rmElemIds.push_back(elem->GetID());
7230           }
7231           else {
7232             rmElemIds.push_back(elem->GetID());
7233           }
7234
7235         }
7236         else if (elem->GetType() == SMDSAbs_Volume) {
7237           // Polyhedral volume
7238           if (nbUniqueNodes < 4) {
7239             rmElemIds.push_back(elem->GetID());
7240           }
7241           else {
7242             // each face has to be analyzed in order to check volume validity
7243             const SMDS_VtkVolume* aPolyedre =
7244               dynamic_cast<const SMDS_VtkVolume*>( elem );
7245             if (aPolyedre) {
7246               int nbFaces = aPolyedre->NbFaces();
7247
7248               vector<const SMDS_MeshNode *> poly_nodes;
7249               vector<int> quantities;
7250
7251               for (int iface = 1; iface <= nbFaces; iface++) {
7252                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7253                 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7254
7255                 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7256                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7257                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7258                   if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7259                     faceNode = (*nnIt).second;
7260                   }
7261                   faceNodes[inode - 1] = faceNode;
7262                 }
7263
7264                 SimplifyFace(faceNodes, poly_nodes, quantities);
7265               }
7266
7267               if (quantities.size() > 3) {
7268                 // to be done: remove coincident faces
7269               }
7270
7271               if (quantities.size() > 3)
7272                 {
7273                   MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7274                   //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7275                   const SMDS_MeshElement* newElem = 0;
7276                   newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7277                   myLastCreatedElems.Append(newElem);
7278                   if ( aShapeId && newElem )
7279                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
7280                   rmElemIds.push_back(elem->GetID());
7281                 }
7282             }
7283             else {
7284               rmElemIds.push_back(elem->GetID());
7285             }
7286           }
7287         }
7288         else {
7289         }
7290
7291         continue;
7292       } // poly element
7293
7294       // Regular elements
7295       // TODO not all the possible cases are solved. Find something more generic?
7296       switch ( nbNodes ) {
7297       case 2: ///////////////////////////////////// EDGE
7298         isOk = false; break;
7299       case 3: ///////////////////////////////////// TRIANGLE
7300         isOk = false; break;
7301       case 4:
7302         if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7303           isOk = false;
7304         else { //////////////////////////////////// QUADRANGLE
7305           if ( nbUniqueNodes < 3 )
7306             isOk = false;
7307           else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7308             isOk = false; // opposite nodes stick
7309           //MESSAGE("isOk " << isOk);
7310         }
7311         break;
7312       case 6: ///////////////////////////////////// PENTAHEDRON
7313         if ( nbUniqueNodes == 4 ) {
7314           // ---------------------------------> tetrahedron
7315           if (nbRepl == 3 &&
7316               iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7317             // all top nodes stick: reverse a bottom
7318             uniqueNodes[ 0 ] = curNodes [ 1 ];
7319             uniqueNodes[ 1 ] = curNodes [ 0 ];
7320           }
7321           else if (nbRepl == 3 &&
7322                    iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7323             // all bottom nodes stick: set a top before
7324             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7325             uniqueNodes[ 0 ] = curNodes [ 3 ];
7326             uniqueNodes[ 1 ] = curNodes [ 4 ];
7327             uniqueNodes[ 2 ] = curNodes [ 5 ];
7328           }
7329           else if (nbRepl == 4 &&
7330                    iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7331             // a lateral face turns into a line: reverse a bottom
7332             uniqueNodes[ 0 ] = curNodes [ 1 ];
7333             uniqueNodes[ 1 ] = curNodes [ 0 ];
7334           }
7335           else
7336             isOk = false;
7337         }
7338         else if ( nbUniqueNodes == 5 ) {
7339           // PENTAHEDRON --------------------> 2 tetrahedrons
7340           if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7341             // a bottom node sticks with a linked top one
7342             // 1.
7343             SMDS_MeshElement* newElem =
7344               aMesh->AddVolume(curNodes[ 3 ],
7345                                curNodes[ 4 ],
7346                                curNodes[ 5 ],
7347                                curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7348             myLastCreatedElems.Append(newElem);
7349             if ( aShapeId )
7350               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7351             // 2. : reverse a bottom
7352             uniqueNodes[ 0 ] = curNodes [ 1 ];
7353             uniqueNodes[ 1 ] = curNodes [ 0 ];
7354             nbUniqueNodes = 4;
7355           }
7356           else
7357             isOk = false;
7358         }
7359         else
7360           isOk = false;
7361         break;
7362       case 8: {
7363         if(elem->IsQuadratic()) { // Quadratic quadrangle
7364           //   1    5    2
7365           //    +---+---+
7366           //    |       |
7367           //    |       |
7368           //   4+       +6
7369           //    |       |
7370           //    |       |
7371           //    +---+---+
7372           //   0    7    3
7373           isOk = false;
7374           if(nbRepl==2) {
7375             MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7376           }
7377           if(nbRepl==3) {
7378             MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2]);
7379             nbUniqueNodes = 6;
7380             if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7381               uniqueNodes[0] = curNodes[0];
7382               uniqueNodes[1] = curNodes[2];
7383               uniqueNodes[2] = curNodes[3];
7384               uniqueNodes[3] = curNodes[5];
7385               uniqueNodes[4] = curNodes[6];
7386               uniqueNodes[5] = curNodes[7];
7387               isOk = true;
7388             }
7389             if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7390               uniqueNodes[0] = curNodes[0];
7391               uniqueNodes[1] = curNodes[1];
7392               uniqueNodes[2] = curNodes[2];
7393               uniqueNodes[3] = curNodes[4];
7394               uniqueNodes[4] = curNodes[5];
7395               uniqueNodes[5] = curNodes[6];
7396               isOk = true;
7397             }
7398             if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7399               uniqueNodes[0] = curNodes[1];
7400               uniqueNodes[1] = curNodes[2];
7401               uniqueNodes[2] = curNodes[3];
7402               uniqueNodes[3] = curNodes[5];
7403               uniqueNodes[4] = curNodes[6];
7404               uniqueNodes[5] = curNodes[0];
7405               isOk = true;
7406             }
7407             if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7408               uniqueNodes[0] = curNodes[0];
7409               uniqueNodes[1] = curNodes[1];
7410               uniqueNodes[2] = curNodes[3];
7411               uniqueNodes[3] = curNodes[4];
7412               uniqueNodes[4] = curNodes[6];
7413               uniqueNodes[5] = curNodes[7];
7414               isOk = true;
7415             }
7416             if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7417               uniqueNodes[0] = curNodes[0];
7418               uniqueNodes[1] = curNodes[2];
7419               uniqueNodes[2] = curNodes[3];
7420               uniqueNodes[3] = curNodes[1];
7421               uniqueNodes[4] = curNodes[6];
7422               uniqueNodes[5] = curNodes[7];
7423               isOk = true;
7424             }
7425             if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7426               uniqueNodes[0] = curNodes[0];
7427               uniqueNodes[1] = curNodes[1];
7428               uniqueNodes[2] = curNodes[2];
7429               uniqueNodes[3] = curNodes[4];
7430               uniqueNodes[4] = curNodes[5];
7431               uniqueNodes[5] = curNodes[7];
7432               isOk = true;
7433             }
7434             if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7435               uniqueNodes[0] = curNodes[0];
7436               uniqueNodes[1] = curNodes[1];
7437               uniqueNodes[2] = curNodes[3];
7438               uniqueNodes[3] = curNodes[4];
7439               uniqueNodes[4] = curNodes[2];
7440               uniqueNodes[5] = curNodes[7];
7441               isOk = true;
7442             }
7443             if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7444               uniqueNodes[0] = curNodes[0];
7445               uniqueNodes[1] = curNodes[1];
7446               uniqueNodes[2] = curNodes[2];
7447               uniqueNodes[3] = curNodes[4];
7448               uniqueNodes[4] = curNodes[5];
7449               uniqueNodes[5] = curNodes[3];
7450               isOk = true;
7451             }
7452           }
7453           if(nbRepl==4) {
7454             MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3]);
7455           }
7456           if(nbRepl==5) {
7457             MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1]  << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7458           }
7459           break;
7460         }
7461         //////////////////////////////////// HEXAHEDRON
7462         isOk = false;
7463         SMDS_VolumeTool hexa (elem);
7464         hexa.SetExternalNormal();
7465         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7466           //////////////////////// HEX ---> 1 tetrahedron
7467           for ( int iFace = 0; iFace < 6; iFace++ ) {
7468             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7469             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7470                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7471                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7472               // one face turns into a point ...
7473               int iOppFace = hexa.GetOppFaceIndex( iFace );
7474               ind = hexa.GetFaceNodesIndices( iOppFace );
7475               int nbStick = 0;
7476               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7477                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7478                   nbStick++;
7479               }
7480               if ( nbStick == 1 ) {
7481                 // ... and the opposite one - into a triangle.
7482                 // set a top node
7483                 ind = hexa.GetFaceNodesIndices( iFace );
7484                 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7485                 isOk = true;
7486               }
7487               break;
7488             }
7489           }
7490         }
7491         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7492           //////////////////////// HEX ---> 1 prism
7493           int nbTria = 0, iTria[3];
7494           const int *ind; // indices of face nodes
7495           // look for triangular faces
7496           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7497             ind = hexa.GetFaceNodesIndices( iFace );
7498             TIDSortedNodeSet faceNodes;
7499             for ( iCur = 0; iCur < 4; iCur++ )
7500               faceNodes.insert( curNodes[ind[iCur]] );
7501             if ( faceNodes.size() == 3 )
7502               iTria[ nbTria++ ] = iFace;
7503           }
7504           // check if triangles are opposite
7505           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7506           {
7507             isOk = true;
7508             // set nodes of the bottom triangle
7509             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7510             vector<int> indB;
7511             for ( iCur = 0; iCur < 4; iCur++ )
7512               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7513                 indB.push_back( ind[iCur] );
7514             if ( !hexa.IsForward() )
7515               std::swap( indB[0], indB[2] );
7516             for ( iCur = 0; iCur < 3; iCur++ )
7517               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7518             // set nodes of the top triangle
7519             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7520             for ( iCur = 0; iCur < 3; ++iCur )
7521               for ( int j = 0; j < 4; ++j )
7522                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7523                 {
7524                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7525                   break;
7526                 }
7527           }
7528           break;
7529         }
7530         else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7531           //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7532           for ( int iFace = 0; iFace < 6; iFace++ ) {
7533             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7534             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7535                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7536                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7537               // one face turns into a point ...
7538               int iOppFace = hexa.GetOppFaceIndex( iFace );
7539               ind = hexa.GetFaceNodesIndices( iOppFace );
7540               int nbStick = 0;
7541               iUnique = 2;  // reverse a tetrahedron 1 bottom
7542               for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7543                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7544                   nbStick++;
7545                 else if ( iUnique >= 0 )
7546                   uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7547               }
7548               if ( nbStick == 0 ) {
7549                 // ... and the opposite one is a quadrangle
7550                 // set a top node
7551                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7552                 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7553                 nbUniqueNodes = 4;
7554                 // tetrahedron 2
7555                 SMDS_MeshElement* newElem =
7556                   aMesh->AddVolume(curNodes[ind[ 0 ]],
7557                                    curNodes[ind[ 3 ]],
7558                                    curNodes[ind[ 2 ]],
7559                                    curNodes[indTop[ 0 ]]);
7560                 myLastCreatedElems.Append(newElem);
7561                 if ( aShapeId )
7562                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7563                 isOk = true;
7564               }
7565               break;
7566             }
7567           }
7568         }
7569         else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7570           ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7571           // find indices of quad and tri faces
7572           int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7573           for ( iFace = 0; iFace < 6; iFace++ ) {
7574             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7575             nodeSet.clear();
7576             for ( iCur = 0; iCur < 4; iCur++ )
7577               nodeSet.insert( curNodes[ind[ iCur ]] );
7578             nbUniqueNodes = nodeSet.size();
7579             if ( nbUniqueNodes == 3 )
7580               iTriFace[ nbTri++ ] = iFace;
7581             else if ( nbUniqueNodes == 4 )
7582               iQuadFace[ nbQuad++ ] = iFace;
7583           }
7584           if (nbQuad == 2 && nbTri == 4 &&
7585               hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7586             // 2 opposite quadrangles stuck with a diagonal;
7587             // sample groups of merged indices: (0-4)(2-6)
7588             // --------------------------------------------> 2 tetrahedrons
7589             const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7590             const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7591             int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7592             if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7593                 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7594               // stuck with 0-2 diagonal
7595               i0  = ind1[ 3 ];
7596               i1d = ind1[ 0 ];
7597               i2  = ind1[ 1 ];
7598               i3d = ind1[ 2 ];
7599               i0t = ind2[ 1 ];
7600               i2t = ind2[ 3 ];
7601             }
7602             else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7603                      curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7604               // stuck with 1-3 diagonal
7605               i0  = ind1[ 0 ];
7606               i1d = ind1[ 1 ];
7607               i2  = ind1[ 2 ];
7608               i3d = ind1[ 3 ];
7609               i0t = ind2[ 0 ];
7610               i2t = ind2[ 1 ];
7611             }
7612             else {
7613               ASSERT(0);
7614             }
7615             // tetrahedron 1
7616             uniqueNodes[ 0 ] = curNodes [ i0 ];
7617             uniqueNodes[ 1 ] = curNodes [ i1d ];
7618             uniqueNodes[ 2 ] = curNodes [ i3d ];
7619             uniqueNodes[ 3 ] = curNodes [ i0t ];
7620             nbUniqueNodes = 4;
7621             // tetrahedron 2
7622             SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7623                                                          curNodes[ i2 ],
7624                                                          curNodes[ i3d ],
7625                                                          curNodes[ i2t ]);
7626             myLastCreatedElems.Append(newElem);
7627             if ( aShapeId )
7628               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7629             isOk = true;
7630           }
7631           else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7632                    ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7633             // --------------------------------------------> prism
7634             // find 2 opposite triangles
7635             nbUniqueNodes = 6;
7636             for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7637               if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7638                 // find indices of kept and replaced nodes
7639                 // and fill unique nodes of 2 opposite triangles
7640                 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7641                 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7642                 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7643                 // fill unique nodes
7644                 iUnique = 0;
7645                 isOk = true;
7646                 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7647                   const SMDS_MeshNode* n     = curNodes[ind1[ iCur ]];
7648                   const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7649                   if ( n == nInit ) {
7650                     // iCur of a linked node of the opposite face (make normals co-directed):
7651                     int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7652                     // check that correspondent corners of triangles are linked
7653                     if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7654                       isOk = false;
7655                     else {
7656                       uniqueNodes[ iUnique ] = n;
7657                       uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7658                       iUnique++;
7659                     }
7660                   }
7661                 }
7662                 break;
7663               }
7664             }
7665           }
7666         } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7667         else
7668         {
7669           MESSAGE("MergeNodes() removes hexahedron "<< elem);
7670         }
7671         break;
7672       } // HEXAHEDRON
7673
7674       default:
7675         isOk = false;
7676       } // switch ( nbNodes )
7677
7678     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7679
7680     if ( isOk ) { // the elem remains valid after sticking nodes
7681       if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7682       {
7683         // Change nodes of polyedre
7684         const SMDS_VtkVolume* aPolyedre =
7685           dynamic_cast<const SMDS_VtkVolume*>( elem );
7686         if (aPolyedre) {
7687           int nbFaces = aPolyedre->NbFaces();
7688
7689           vector<const SMDS_MeshNode *> poly_nodes;
7690           vector<int> quantities (nbFaces);
7691
7692           for (int iface = 1; iface <= nbFaces; iface++) {
7693             int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7694             quantities[iface - 1] = nbFaceNodes;
7695
7696             for (inode = 1; inode <= nbFaceNodes; inode++) {
7697               const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7698
7699               TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7700               if (nnIt != nodeNodeMap.end()) { // curNode sticks
7701                 curNode = (*nnIt).second;
7702               }
7703               poly_nodes.push_back(curNode);
7704             }
7705           }
7706           aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7707         }
7708       }
7709       else // replace non-polyhedron elements
7710       {
7711         const SMDSAbs_ElementType etyp = elem->GetType();
7712         const int elemId               = elem->GetID();
7713         const bool isPoly              = (elem->GetEntityType() == SMDSEntity_Polygon);
7714         uniqueNodes.resize(nbUniqueNodes);
7715
7716         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7717
7718         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7719         SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7720         if ( sm && newElem )
7721           sm->AddElement( newElem );
7722         if ( elem != newElem )
7723           ReplaceElemInGroups( elem, newElem, aMesh );
7724       }
7725     }
7726     else {
7727       // Remove invalid regular element or invalid polygon
7728       rmElemIds.push_back( elem->GetID() );
7729     }
7730
7731   } // loop on elements
7732
7733   // Remove bad elements, then equal nodes (order important)
7734
7735   Remove( rmElemIds, false );
7736   Remove( rmNodeIds, true );
7737
7738 }
7739
7740
7741 // ========================================================
7742 // class   : SortableElement
7743 // purpose : allow sorting elements basing on their nodes
7744 // ========================================================
7745 class SortableElement : public set <const SMDS_MeshElement*>
7746 {
7747 public:
7748
7749   SortableElement( const SMDS_MeshElement* theElem )
7750   {
7751     myElem = theElem;
7752     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7753     while ( nodeIt->more() )
7754       this->insert( nodeIt->next() );
7755   }
7756
7757   const SMDS_MeshElement* Get() const
7758   { return myElem; }
7759
7760   void Set(const SMDS_MeshElement* e) const
7761   { myElem = e; }
7762
7763
7764 private:
7765   mutable const SMDS_MeshElement* myElem;
7766 };
7767
7768 //=======================================================================
7769 //function : FindEqualElements
7770 //purpose  : Return list of group of elements built on the same nodes.
7771 //           Search among theElements or in the whole mesh if theElements is empty
7772 //=======================================================================
7773
7774 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7775                                          TListOfListOfElementsID & theGroupsOfElementsID)
7776 {
7777   myLastCreatedElems.Clear();
7778   myLastCreatedNodes.Clear();
7779
7780   typedef map< SortableElement, int > TMapOfNodeSet;
7781   typedef list<int> TGroupOfElems;
7782
7783   if ( theElements.empty() )
7784   { // get all elements in the mesh
7785     SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7786     while ( eIt->more() )
7787       theElements.insert( theElements.end(), eIt->next());
7788   }
7789
7790   vector< TGroupOfElems > arrayOfGroups;
7791   TGroupOfElems groupOfElems;
7792   TMapOfNodeSet mapOfNodeSet;
7793
7794   TIDSortedElemSet::iterator elemIt = theElements.begin();
7795   for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7796     const SMDS_MeshElement* curElem = *elemIt;
7797     SortableElement SE(curElem);
7798     int ind = -1;
7799     // check uniqueness
7800     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7801     if( !(pp.second) ) {
7802       TMapOfNodeSet::iterator& itSE = pp.first;
7803       ind = (*itSE).second;
7804       arrayOfGroups[ind].push_back(curElem->GetID());
7805     }
7806     else {
7807       groupOfElems.clear();
7808       groupOfElems.push_back(curElem->GetID());
7809       arrayOfGroups.push_back(groupOfElems);
7810       i++;
7811     }
7812   }
7813
7814   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7815   for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7816     groupOfElems = *groupIt;
7817     if ( groupOfElems.size() > 1 ) {
7818       groupOfElems.sort();
7819       theGroupsOfElementsID.push_back(groupOfElems);
7820     }
7821   }
7822 }
7823
7824 //=======================================================================
7825 //function : MergeElements
7826 //purpose  : In each given group, substitute all elements by the first one.
7827 //=======================================================================
7828
7829 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7830 {
7831   myLastCreatedElems.Clear();
7832   myLastCreatedNodes.Clear();
7833
7834   typedef list<int> TListOfIDs;
7835   TListOfIDs rmElemIds; // IDs of elems to remove
7836
7837   SMESHDS_Mesh* aMesh = GetMeshDS();
7838
7839   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7840   while ( groupsIt != theGroupsOfElementsID.end() ) {
7841     TListOfIDs& aGroupOfElemID = *groupsIt;
7842     aGroupOfElemID.sort();
7843     int elemIDToKeep = aGroupOfElemID.front();
7844     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7845     aGroupOfElemID.pop_front();
7846     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7847     while ( idIt != aGroupOfElemID.end() ) {
7848       int elemIDToRemove = *idIt;
7849       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7850       // add the kept element in groups of removed one (PAL15188)
7851       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7852       rmElemIds.push_back( elemIDToRemove );
7853       ++idIt;
7854     }
7855     ++groupsIt;
7856   }
7857
7858   Remove( rmElemIds, false );
7859 }
7860
7861 //=======================================================================
7862 //function : MergeEqualElements
7863 //purpose  : Remove all but one of elements built on the same nodes.
7864 //=======================================================================
7865
7866 void SMESH_MeshEditor::MergeEqualElements()
7867 {
7868   TIDSortedElemSet aMeshElements; /* empty input ==
7869                                      to merge equal elements in the whole mesh */
7870   TListOfListOfElementsID aGroupsOfElementsID;
7871   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7872   MergeElements(aGroupsOfElementsID);
7873 }
7874
7875 //=======================================================================
7876 //function : findAdjacentFace
7877 //purpose  :
7878 //=======================================================================
7879
7880 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7881                                                 const SMDS_MeshNode* n2,
7882                                                 const SMDS_MeshElement* elem)
7883 {
7884   TIDSortedElemSet elemSet, avoidSet;
7885   if ( elem )
7886     avoidSet.insert ( elem );
7887   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7888 }
7889
7890 //=======================================================================
7891 //function : FindFreeBorder
7892 //purpose  :
7893 //=======================================================================
7894
7895 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7896
7897 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7898                                        const SMDS_MeshNode*             theSecondNode,
7899                                        const SMDS_MeshNode*             theLastNode,
7900                                        list< const SMDS_MeshNode* > &   theNodes,
7901                                        list< const SMDS_MeshElement* >& theFaces)
7902 {
7903   if ( !theFirstNode || !theSecondNode )
7904     return false;
7905   // find border face between theFirstNode and theSecondNode
7906   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7907   if ( !curElem )
7908     return false;
7909
7910   theFaces.push_back( curElem );
7911   theNodes.push_back( theFirstNode );
7912   theNodes.push_back( theSecondNode );
7913
7914   //vector<const SMDS_MeshNode*> nodes;
7915   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7916   TIDSortedElemSet foundElems;
7917   bool needTheLast = ( theLastNode != 0 );
7918
7919   while ( nStart != theLastNode ) {
7920     if ( nStart == theFirstNode )
7921       return !needTheLast;
7922
7923     // find all free border faces sharing form nStart
7924
7925     list< const SMDS_MeshElement* > curElemList;
7926     list< const SMDS_MeshNode* > nStartList;
7927     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7928     while ( invElemIt->more() ) {
7929       const SMDS_MeshElement* e = invElemIt->next();
7930       if ( e == curElem || foundElems.insert( e ).second ) {
7931         // get nodes
7932         int iNode = 0, nbNodes = e->NbNodes();
7933         //const SMDS_MeshNode* nodes[nbNodes+1];
7934         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7935
7936         if(e->IsQuadratic()) {
7937           const SMDS_VtkFace* F =
7938             dynamic_cast<const SMDS_VtkFace*>(e);
7939           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7940           // use special nodes iterator
7941           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7942           while( anIter->more() ) {
7943             nodes[ iNode++ ] = cast2Node(anIter->next());
7944           }
7945         }
7946         else {
7947           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7948           while ( nIt->more() )
7949             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7950         }
7951         nodes[ iNode ] = nodes[ 0 ];
7952         // check 2 links
7953         for ( iNode = 0; iNode < nbNodes; iNode++ )
7954           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7955                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7956               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7957           {
7958             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7959             curElemList.push_back( e );
7960           }
7961       }
7962     }
7963     // analyse the found
7964
7965     int nbNewBorders = curElemList.size();
7966     if ( nbNewBorders == 0 ) {
7967       // no free border furthermore
7968       return !needTheLast;
7969     }
7970     else if ( nbNewBorders == 1 ) {
7971       // one more element found
7972       nIgnore = nStart;
7973       nStart = nStartList.front();
7974       curElem = curElemList.front();
7975       theFaces.push_back( curElem );
7976       theNodes.push_back( nStart );
7977     }
7978     else {
7979       // several continuations found
7980       list< const SMDS_MeshElement* >::iterator curElemIt;
7981       list< const SMDS_MeshNode* >::iterator nStartIt;
7982       // check if one of them reached the last node
7983       if ( needTheLast ) {
7984         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7985              curElemIt!= curElemList.end();
7986              curElemIt++, nStartIt++ )
7987           if ( *nStartIt == theLastNode ) {
7988             theFaces.push_back( *curElemIt );
7989             theNodes.push_back( *nStartIt );
7990             return true;
7991           }
7992       }
7993       // find the best free border by the continuations
7994       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7995       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7996       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7997            curElemIt!= curElemList.end();
7998            curElemIt++, nStartIt++ )
7999       {
8000         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8001         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8002         // find one more free border
8003         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8004           cNL->clear();
8005           cFL->clear();
8006         }
8007         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8008           // choice: clear a worse one
8009           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8010           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8011           contNodes[ iWorse ].clear();
8012           contFaces[ iWorse ].clear();
8013         }
8014       }
8015       if ( contNodes[0].empty() && contNodes[1].empty() )
8016         return false;
8017
8018       // append the best free border
8019       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8020       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8021       theNodes.pop_back(); // remove nIgnore
8022       theNodes.pop_back(); // remove nStart
8023       theFaces.pop_back(); // remove curElem
8024       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8025       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8026       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8027       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8028       return true;
8029
8030     } // several continuations found
8031   } // while ( nStart != theLastNode )
8032
8033   return true;
8034 }
8035
8036 //=======================================================================
8037 //function : CheckFreeBorderNodes
8038 //purpose  : Return true if the tree nodes are on a free border
8039 //=======================================================================
8040
8041 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8042                                             const SMDS_MeshNode* theNode2,
8043                                             const SMDS_MeshNode* theNode3)
8044 {
8045   list< const SMDS_MeshNode* > nodes;
8046   list< const SMDS_MeshElement* > faces;
8047   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8048 }
8049
8050 //=======================================================================
8051 //function : SewFreeBorder
8052 //purpose  :
8053 //=======================================================================
8054
8055 SMESH_MeshEditor::Sew_Error
8056 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8057                                  const SMDS_MeshNode* theBordSecondNode,
8058                                  const SMDS_MeshNode* theBordLastNode,
8059                                  const SMDS_MeshNode* theSideFirstNode,
8060                                  const SMDS_MeshNode* theSideSecondNode,
8061                                  const SMDS_MeshNode* theSideThirdNode,
8062                                  const bool           theSideIsFreeBorder,
8063                                  const bool           toCreatePolygons,
8064                                  const bool           toCreatePolyedrs)
8065 {
8066   myLastCreatedElems.Clear();
8067   myLastCreatedNodes.Clear();
8068
8069   MESSAGE("::SewFreeBorder()");
8070   Sew_Error aResult = SEW_OK;
8071
8072   // ====================================
8073   //    find side nodes and elements
8074   // ====================================
8075
8076   list< const SMDS_MeshNode* > nSide[ 2 ];
8077   list< const SMDS_MeshElement* > eSide[ 2 ];
8078   list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8079   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8080
8081   // Free border 1
8082   // --------------
8083   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8084                       nSide[0], eSide[0])) {
8085     MESSAGE(" Free Border 1 not found " );
8086     aResult = SEW_BORDER1_NOT_FOUND;
8087   }
8088   if (theSideIsFreeBorder) {
8089     // Free border 2
8090     // --------------
8091     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8092                         nSide[1], eSide[1])) {
8093       MESSAGE(" Free Border 2 not found " );
8094       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8095     }
8096   }
8097   if ( aResult != SEW_OK )
8098     return aResult;
8099
8100   if (!theSideIsFreeBorder) {
8101     // Side 2
8102     // --------------
8103
8104     // -------------------------------------------------------------------------
8105     // Algo:
8106     // 1. If nodes to merge are not coincident, move nodes of the free border
8107     //    from the coord sys defined by the direction from the first to last
8108     //    nodes of the border to the correspondent sys of the side 2
8109     // 2. On the side 2, find the links most co-directed with the correspondent
8110     //    links of the free border
8111     // -------------------------------------------------------------------------
8112
8113     // 1. Since sewing may break if there are volumes to split on the side 2,
8114     //    we wont move nodes but just compute new coordinates for them
8115     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8116     TNodeXYZMap nBordXYZ;
8117     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8118     list< const SMDS_MeshNode* >::iterator nBordIt;
8119
8120     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8121     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8122     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8123     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8124     double tol2 = 1.e-8;
8125     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8126     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8127       // Need node movement.
8128
8129       // find X and Z axes to create trsf
8130       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8131       gp_Vec X = Zs ^ Zb;
8132       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8133         // Zb || Zs
8134         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8135
8136       // coord systems
8137       gp_Ax3 toBordAx( Pb1, Zb, X );
8138       gp_Ax3 fromSideAx( Ps1, Zs, X );
8139       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8140       // set trsf
8141       gp_Trsf toBordSys, fromSide2Sys;
8142       toBordSys.SetTransformation( toBordAx );
8143       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8144       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8145
8146       // move
8147       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8148         const SMDS_MeshNode* n = *nBordIt;
8149         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8150         toBordSys.Transforms( xyz );
8151         fromSide2Sys.Transforms( xyz );
8152         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8153       }
8154     }
8155     else {
8156       // just insert nodes XYZ in the nBordXYZ map
8157       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8158         const SMDS_MeshNode* n = *nBordIt;
8159         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8160       }
8161     }
8162
8163     // 2. On the side 2, find the links most co-directed with the correspondent
8164     //    links of the free border
8165
8166     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8167     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8168     sideNodes.push_back( theSideFirstNode );
8169
8170     bool hasVolumes = false;
8171     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8172     set<long> foundSideLinkIDs, checkedLinkIDs;
8173     SMDS_VolumeTool volume;
8174     //const SMDS_MeshNode* faceNodes[ 4 ];
8175
8176     const SMDS_MeshNode*    sideNode;
8177     const SMDS_MeshElement* sideElem;
8178     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8179     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8180     nBordIt = bordNodes.begin();
8181     nBordIt++;
8182     // border node position and border link direction to compare with
8183     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8184     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8185     // choose next side node by link direction or by closeness to
8186     // the current border node:
8187     bool searchByDir = ( *nBordIt != theBordLastNode );
8188     do {
8189       // find the next node on the Side 2
8190       sideNode = 0;
8191       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8192       long linkID;
8193       checkedLinkIDs.clear();
8194       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8195
8196       // loop on inverse elements of current node (prevSideNode) on the Side 2
8197       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8198       while ( invElemIt->more() )
8199       {
8200         const SMDS_MeshElement* elem = invElemIt->next();
8201         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8202         int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8203         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8204         bool isVolume = volume.Set( elem );
8205         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8206         if ( isVolume ) // --volume
8207           hasVolumes = true;
8208         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8209           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8210           if(elem->IsQuadratic()) {
8211             const SMDS_VtkFace* F =
8212               dynamic_cast<const SMDS_VtkFace*>(elem);
8213             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8214             // use special nodes iterator
8215             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8216             while( anIter->more() ) {
8217               nodes[ iNode ] = cast2Node(anIter->next());
8218               if ( nodes[ iNode++ ] == prevSideNode )
8219                 iPrevNode = iNode - 1;
8220             }
8221           }
8222           else {
8223             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8224             while ( nIt->more() ) {
8225               nodes[ iNode ] = cast2Node( nIt->next() );
8226               if ( nodes[ iNode++ ] == prevSideNode )
8227                 iPrevNode = iNode - 1;
8228             }
8229           }
8230           // there are 2 links to check
8231           nbNodes = 2;
8232         }
8233         else // --edge
8234           continue;
8235         // loop on links, to be precise, on the second node of links
8236         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8237           const SMDS_MeshNode* n = nodes[ iNode ];
8238           if ( isVolume ) {
8239             if ( !volume.IsLinked( n, prevSideNode ))
8240               continue;
8241           }
8242           else {
8243             if ( iNode ) // a node before prevSideNode
8244               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8245             else         // a node after prevSideNode
8246               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8247           }
8248           // check if this link was already used
8249           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8250           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8251           if (!isJustChecked &&
8252               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8253           {
8254             // test a link geometrically
8255             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8256             bool linkIsBetter = false;
8257             double dot = 0.0, dist = 0.0;
8258             if ( searchByDir ) { // choose most co-directed link
8259               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8260               linkIsBetter = ( dot > maxDot );
8261             }
8262             else { // choose link with the node closest to bordPos
8263               dist = ( nextXYZ - bordPos ).SquareModulus();
8264               linkIsBetter = ( dist < minDist );
8265             }
8266             if ( linkIsBetter ) {
8267               maxDot = dot;
8268               minDist = dist;
8269               linkID = iLink;
8270               sideNode = n;
8271               sideElem = elem;
8272             }
8273           }
8274         }
8275       } // loop on inverse elements of prevSideNode
8276
8277       if ( !sideNode ) {
8278         MESSAGE(" Cant find path by links of the Side 2 ");
8279         return SEW_BAD_SIDE_NODES;
8280       }
8281       sideNodes.push_back( sideNode );
8282       sideElems.push_back( sideElem );
8283       foundSideLinkIDs.insert ( linkID );
8284       prevSideNode = sideNode;
8285
8286       if ( *nBordIt == theBordLastNode )
8287         searchByDir = false;
8288       else {
8289         // find the next border link to compare with
8290         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8291         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8292         // move to next border node if sideNode is before forward border node (bordPos)
8293         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8294           prevBordNode = *nBordIt;
8295           nBordIt++;
8296           bordPos = nBordXYZ[ *nBordIt ];
8297           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8298           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8299         }
8300       }
8301     }
8302     while ( sideNode != theSideSecondNode );
8303
8304     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8305       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8306       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8307     }
8308   } // end nodes search on the side 2
8309
8310   // ============================
8311   // sew the border to the side 2
8312   // ============================
8313
8314   int nbNodes[]  = { nSide[0].size(), nSide[1].size() };
8315   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8316
8317   TListOfListOfNodes nodeGroupsToMerge;
8318   if ( nbNodes[0] == nbNodes[1] ||
8319        ( theSideIsFreeBorder && !theSideThirdNode)) {
8320
8321     // all nodes are to be merged
8322
8323     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8324          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8325          nIt[0]++, nIt[1]++ )
8326     {
8327       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8328       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8329       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8330     }
8331   }
8332   else {
8333
8334     // insert new nodes into the border and the side to get equal nb of segments
8335
8336     // get normalized parameters of nodes on the borders
8337     //double param[ 2 ][ maxNbNodes ];
8338     double* param[ 2 ];
8339     param[0] = new double [ maxNbNodes ];
8340     param[1] = new double [ maxNbNodes ];
8341     int iNode, iBord;
8342     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8343       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8344       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8345       const SMDS_MeshNode* nPrev = *nIt;
8346       double bordLength = 0;
8347       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8348         const SMDS_MeshNode* nCur = *nIt;
8349         gp_XYZ segment (nCur->X() - nPrev->X(),
8350                         nCur->Y() - nPrev->Y(),
8351                         nCur->Z() - nPrev->Z());
8352         double segmentLen = segment.Modulus();
8353         bordLength += segmentLen;
8354         param[ iBord ][ iNode ] = bordLength;
8355         nPrev = nCur;
8356       }
8357       // normalize within [0,1]
8358       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8359         param[ iBord ][ iNode ] /= bordLength;
8360       }
8361     }
8362
8363     // loop on border segments
8364     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8365     int i[ 2 ] = { 0, 0 };
8366     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8367     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8368
8369     TElemOfNodeListMap insertMap;
8370     TElemOfNodeListMap::iterator insertMapIt;
8371     // insertMap is
8372     // key:   elem to insert nodes into
8373     // value: 2 nodes to insert between + nodes to be inserted
8374     do {
8375       bool next[ 2 ] = { false, false };
8376
8377       // find min adjacent segment length after sewing
8378       double nextParam = 10., prevParam = 0;
8379       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8380         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8381           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8382         if ( i[ iBord ] > 0 )
8383           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8384       }
8385       double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8386       double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8387       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8388
8389       // choose to insert or to merge nodes
8390       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8391       if ( Abs( du ) <= minSegLen * 0.2 ) {
8392         // merge
8393         // ------
8394         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8395         const SMDS_MeshNode* n0 = *nIt[0];
8396         const SMDS_MeshNode* n1 = *nIt[1];
8397         nodeGroupsToMerge.back().push_back( n1 );
8398         nodeGroupsToMerge.back().push_back( n0 );
8399         // position of node of the border changes due to merge
8400         param[ 0 ][ i[0] ] += du;
8401         // move n1 for the sake of elem shape evaluation during insertion.
8402         // n1 will be removed by MergeNodes() anyway
8403         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8404         next[0] = next[1] = true;
8405       }
8406       else {
8407         // insert
8408         // ------
8409         int intoBord = ( du < 0 ) ? 0 : 1;
8410         const SMDS_MeshElement* elem = *eIt[ intoBord ];
8411         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8412         const SMDS_MeshNode*    n2   = *nIt[ intoBord ];
8413         const SMDS_MeshNode*    nIns = *nIt[ 1 - intoBord ];
8414         if ( intoBord == 1 ) {
8415           // move node of the border to be on a link of elem of the side
8416           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8417           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8418           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8419           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8420           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8421         }
8422         insertMapIt = insertMap.find( elem );
8423         bool notFound = ( insertMapIt == insertMap.end() );
8424         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8425         if ( otherLink ) {
8426           // insert into another link of the same element:
8427           // 1. perform insertion into the other link of the elem
8428           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8429           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8430           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8431           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8432           // 2. perform insertion into the link of adjacent faces
8433           while (true) {
8434             const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8435             if ( adjElem )
8436               InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8437             else
8438               break;
8439           }
8440           if (toCreatePolyedrs) {
8441             // perform insertion into the links of adjacent volumes
8442             UpdateVolumes(n12, n22, nodeList);
8443           }
8444           // 3. find an element appeared on n1 and n2 after the insertion
8445           insertMap.erase( elem );
8446           elem = findAdjacentFace( n1, n2, 0 );
8447         }
8448         if ( notFound || otherLink ) {
8449           // add element and nodes of the side into the insertMap
8450           insertMapIt = insertMap.insert
8451             ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8452           (*insertMapIt).second.push_back( n1 );
8453           (*insertMapIt).second.push_back( n2 );
8454         }
8455         // add node to be inserted into elem
8456         (*insertMapIt).second.push_back( nIns );
8457         next[ 1 - intoBord ] = true;
8458       }
8459
8460       // go to the next segment
8461       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8462         if ( next[ iBord ] ) {
8463           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8464             eIt[ iBord ]++;
8465           nPrev[ iBord ] = *nIt[ iBord ];
8466           nIt[ iBord ]++; i[ iBord ]++;
8467         }
8468       }
8469     }
8470     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8471
8472     // perform insertion of nodes into elements
8473
8474     for (insertMapIt = insertMap.begin();
8475          insertMapIt != insertMap.end();
8476          insertMapIt++ )
8477     {
8478       const SMDS_MeshElement* elem = (*insertMapIt).first;
8479       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8480       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8481       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8482
8483       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8484
8485       if ( !theSideIsFreeBorder ) {
8486         // look for and insert nodes into the faces adjacent to elem
8487         while (true) {
8488           const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8489           if ( adjElem )
8490             InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8491           else
8492             break;
8493         }
8494       }
8495       if (toCreatePolyedrs) {
8496         // perform insertion into the links of adjacent volumes
8497         UpdateVolumes(n1, n2, nodeList);
8498       }
8499     }
8500
8501     delete param[0];
8502     delete param[1];
8503   } // end: insert new nodes
8504
8505   MergeNodes ( nodeGroupsToMerge );
8506
8507   return aResult;
8508 }
8509
8510 //=======================================================================
8511 //function : InsertNodesIntoLink
8512 //purpose  : insert theNodesToInsert into theFace between theBetweenNode1
8513 //           and theBetweenNode2 and split theElement
8514 //=======================================================================
8515
8516 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theFace,
8517                                            const SMDS_MeshNode*        theBetweenNode1,
8518                                            const SMDS_MeshNode*        theBetweenNode2,
8519                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8520                                            const bool                  toCreatePoly)
8521 {
8522   if ( theFace->GetType() != SMDSAbs_Face ) return;
8523
8524   // find indices of 2 link nodes and of the rest nodes
8525   int iNode = 0, il1, il2, i3, i4;
8526   il1 = il2 = i3 = i4 = -1;
8527   //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8528   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8529
8530   if(theFace->IsQuadratic()) {
8531     const SMDS_VtkFace* F =
8532       dynamic_cast<const SMDS_VtkFace*>(theFace);
8533     if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8534     // use special nodes iterator
8535     SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8536     while( anIter->more() ) {
8537       const SMDS_MeshNode* n = cast2Node(anIter->next());
8538       if ( n == theBetweenNode1 )
8539         il1 = iNode;
8540       else if ( n == theBetweenNode2 )
8541         il2 = iNode;
8542       else if ( i3 < 0 )
8543         i3 = iNode;
8544       else
8545         i4 = iNode;
8546       nodes[ iNode++ ] = n;
8547     }
8548   }
8549   else {
8550     SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8551     while ( nodeIt->more() ) {
8552       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8553       if ( n == theBetweenNode1 )
8554         il1 = iNode;
8555       else if ( n == theBetweenNode2 )
8556         il2 = iNode;
8557       else if ( i3 < 0 )
8558         i3 = iNode;
8559       else
8560         i4 = iNode;
8561       nodes[ iNode++ ] = n;
8562     }
8563   }
8564   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8565     return ;
8566
8567   // arrange link nodes to go one after another regarding the face orientation
8568   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8569   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8570   if ( reverse ) {
8571     iNode = il1;
8572     il1 = il2;
8573     il2 = iNode;
8574     aNodesToInsert.reverse();
8575   }
8576   // check that not link nodes of a quadrangles are in good order
8577   int nbFaceNodes = theFace->NbNodes();
8578   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8579     iNode = i3;
8580     i3 = i4;
8581     i4 = iNode;
8582   }
8583
8584   if (toCreatePoly || theFace->IsPoly()) {
8585
8586     iNode = 0;
8587     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8588
8589     // add nodes of face up to first node of link
8590     bool isFLN = false;
8591
8592     if(theFace->IsQuadratic()) {
8593       const SMDS_VtkFace* F =
8594         dynamic_cast<const SMDS_VtkFace*>(theFace);
8595       if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8596       // use special nodes iterator
8597       SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8598       while( anIter->more()  && !isFLN ) {
8599         const SMDS_MeshNode* n = cast2Node(anIter->next());
8600         poly_nodes[iNode++] = n;
8601         if (n == nodes[il1]) {
8602           isFLN = true;
8603         }
8604       }
8605       // add nodes to insert
8606       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8607       for (; nIt != aNodesToInsert.end(); nIt++) {
8608         poly_nodes[iNode++] = *nIt;
8609       }
8610       // add nodes of face starting from last node of link
8611       while ( anIter->more() ) {
8612         poly_nodes[iNode++] = cast2Node(anIter->next());
8613       }
8614     }
8615     else {
8616       SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8617       while ( nodeIt->more() && !isFLN ) {
8618         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8619         poly_nodes[iNode++] = n;
8620         if (n == nodes[il1]) {
8621           isFLN = true;
8622         }
8623       }
8624       // add nodes to insert
8625       list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8626       for (; nIt != aNodesToInsert.end(); nIt++) {
8627         poly_nodes[iNode++] = *nIt;
8628       }
8629       // add nodes of face starting from last node of link
8630       while ( nodeIt->more() ) {
8631         const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8632         poly_nodes[iNode++] = n;
8633       }
8634     }
8635
8636     // edit or replace the face
8637     SMESHDS_Mesh *aMesh = GetMeshDS();
8638
8639     if (theFace->IsPoly()) {
8640       aMesh->ChangePolygonNodes(theFace, poly_nodes);
8641     }
8642     else {
8643       int aShapeId = FindShape( theFace );
8644
8645       SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8646       myLastCreatedElems.Append(newElem);
8647       if ( aShapeId && newElem )
8648         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8649
8650       aMesh->RemoveElement(theFace);
8651     }
8652     return;
8653   }
8654
8655   SMESHDS_Mesh *aMesh = GetMeshDS();
8656   if( !theFace->IsQuadratic() ) {
8657
8658     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8659     int nbLinkNodes = 2 + aNodesToInsert.size();
8660     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8661     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8662     linkNodes[ 0 ] = nodes[ il1 ];
8663     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8664     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8665     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8666       linkNodes[ iNode++ ] = *nIt;
8667     }
8668     // decide how to split a quadrangle: compare possible variants
8669     // and choose which of splits to be a quadrangle
8670     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8671     if ( nbFaceNodes == 3 ) {
8672       iBestQuad = nbSplits;
8673       i4 = i3;
8674     }
8675     else if ( nbFaceNodes == 4 ) {
8676       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8677       double aBestRate = DBL_MAX;
8678       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8679         i1 = 0; i2 = 1;
8680         double aBadRate = 0;
8681         // evaluate elements quality
8682         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8683           if ( iSplit == iQuad ) {
8684             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8685                                    linkNodes[ i2++ ],
8686                                    nodes[ i3 ],
8687                                    nodes[ i4 ]);
8688             aBadRate += getBadRate( &quad, aCrit );
8689           }
8690           else {
8691             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8692                                    linkNodes[ i2++ ],
8693                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8694             aBadRate += getBadRate( &tria, aCrit );
8695           }
8696         }
8697         // choice
8698         if ( aBadRate < aBestRate ) {
8699           iBestQuad = iQuad;
8700           aBestRate = aBadRate;
8701         }
8702       }
8703     }
8704
8705     // create new elements
8706     int aShapeId = FindShape( theFace );
8707
8708     i1 = 0; i2 = 1;
8709     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8710       SMDS_MeshElement* newElem = 0;
8711       if ( iSplit == iBestQuad )
8712         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8713                                   linkNodes[ i2++ ],
8714                                   nodes[ i3 ],
8715                                   nodes[ i4 ]);
8716       else
8717         newElem = aMesh->AddFace (linkNodes[ i1++ ],
8718                                   linkNodes[ i2++ ],
8719                                   nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8720       myLastCreatedElems.Append(newElem);
8721       if ( aShapeId && newElem )
8722         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8723     }
8724
8725     // change nodes of theFace
8726     const SMDS_MeshNode* newNodes[ 4 ];
8727     newNodes[ 0 ] = linkNodes[ i1 ];
8728     newNodes[ 1 ] = linkNodes[ i2 ];
8729     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8730     newNodes[ 3 ] = nodes[ i4 ];
8731     //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8732     const SMDS_MeshElement* newElem = 0;
8733     if (iSplit == iBestQuad)
8734       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8735     else
8736       newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8737     myLastCreatedElems.Append(newElem);
8738     if ( aShapeId && newElem )
8739       aMesh->SetMeshElementOnShape( newElem, aShapeId );
8740 } // end if(!theFace->IsQuadratic())
8741   else { // theFace is quadratic
8742     // we have to split theFace on simple triangles and one simple quadrangle
8743     int tmp = il1/2;
8744     int nbshift = tmp*2;
8745     // shift nodes in nodes[] by nbshift
8746     int i,j;
8747     for(i=0; i<nbshift; i++) {
8748       const SMDS_MeshNode* n = nodes[0];
8749       for(j=0; j<nbFaceNodes-1; j++) {
8750         nodes[j] = nodes[j+1];
8751       }
8752       nodes[nbFaceNodes-1] = n;
8753     }
8754     il1 = il1 - nbshift;
8755     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8756     //   n0      n1     n2    n0      n1     n2
8757     //     +-----+-----+        +-----+-----+
8758     //      \         /         |           |
8759     //       \       /          |           |
8760     //      n5+     +n3       n7+           +n3
8761     //         \   /            |           |
8762     //          \ /             |           |
8763     //           +              +-----+-----+
8764     //           n4           n6      n5     n4
8765
8766     // create new elements
8767     int aShapeId = FindShape( theFace );
8768
8769     int n1,n2,n3;
8770     if(nbFaceNodes==6) { // quadratic triangle
8771       SMDS_MeshElement* newElem =
8772         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8773       myLastCreatedElems.Append(newElem);
8774       if ( aShapeId && newElem )
8775         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8776       if(theFace->IsMediumNode(nodes[il1])) {
8777         // create quadrangle
8778         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8779         myLastCreatedElems.Append(newElem);
8780         if ( aShapeId && newElem )
8781           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8782         n1 = 1;
8783         n2 = 2;
8784         n3 = 3;
8785       }
8786       else {
8787         // create quadrangle
8788         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8789         myLastCreatedElems.Append(newElem);
8790         if ( aShapeId && newElem )
8791           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8792         n1 = 0;
8793         n2 = 1;
8794         n3 = 5;
8795       }
8796     }
8797     else { // nbFaceNodes==8 - quadratic quadrangle
8798       SMDS_MeshElement* newElem =
8799         aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8800       myLastCreatedElems.Append(newElem);
8801       if ( aShapeId && newElem )
8802         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8803       newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8804       myLastCreatedElems.Append(newElem);
8805       if ( aShapeId && newElem )
8806         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8807       newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8808       myLastCreatedElems.Append(newElem);
8809       if ( aShapeId && newElem )
8810         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8811       if(theFace->IsMediumNode(nodes[il1])) {
8812         // create quadrangle
8813         newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8814         myLastCreatedElems.Append(newElem);
8815         if ( aShapeId && newElem )
8816           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8817         n1 = 1;
8818         n2 = 2;
8819         n3 = 3;
8820       }
8821       else {
8822         // create quadrangle
8823         newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8824         myLastCreatedElems.Append(newElem);
8825         if ( aShapeId && newElem )
8826           aMesh->SetMeshElementOnShape( newElem, aShapeId );
8827         n1 = 0;
8828         n2 = 1;
8829         n3 = 7;
8830       }
8831     }
8832     // create needed triangles using n1,n2,n3 and inserted nodes
8833     int nbn = 2 + aNodesToInsert.size();
8834     //const SMDS_MeshNode* aNodes[nbn];
8835     vector<const SMDS_MeshNode*> aNodes(nbn);
8836     aNodes[0] = nodes[n1];
8837     aNodes[nbn-1] = nodes[n2];
8838     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8839     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8840       aNodes[iNode++] = *nIt;
8841     }
8842     for(i=1; i<nbn; i++) {
8843       SMDS_MeshElement* newElem =
8844         aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8845       myLastCreatedElems.Append(newElem);
8846       if ( aShapeId && newElem )
8847         aMesh->SetMeshElementOnShape( newElem, aShapeId );
8848     }
8849   }
8850   // remove old face
8851   aMesh->RemoveElement(theFace);
8852 }
8853
8854 //=======================================================================
8855 //function : UpdateVolumes
8856 //purpose  :
8857 //=======================================================================
8858 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8859                                       const SMDS_MeshNode*        theBetweenNode2,
8860                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8861 {
8862   myLastCreatedElems.Clear();
8863   myLastCreatedNodes.Clear();
8864
8865   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8866   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8867     const SMDS_MeshElement* elem = invElemIt->next();
8868
8869     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8870     SMDS_VolumeTool aVolume (elem);
8871     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8872       continue;
8873
8874     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8875     int iface, nbFaces = aVolume.NbFaces();
8876     vector<const SMDS_MeshNode *> poly_nodes;
8877     vector<int> quantities (nbFaces);
8878
8879     for (iface = 0; iface < nbFaces; iface++) {
8880       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8881       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8882       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8883
8884       for (int inode = 0; inode < nbFaceNodes; inode++) {
8885         poly_nodes.push_back(faceNodes[inode]);
8886
8887         if (nbInserted == 0) {
8888           if (faceNodes[inode] == theBetweenNode1) {
8889             if (faceNodes[inode + 1] == theBetweenNode2) {
8890               nbInserted = theNodesToInsert.size();
8891
8892               // add nodes to insert
8893               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8894               for (; nIt != theNodesToInsert.end(); nIt++) {
8895                 poly_nodes.push_back(*nIt);
8896               }
8897             }
8898           }
8899           else if (faceNodes[inode] == theBetweenNode2) {
8900             if (faceNodes[inode + 1] == theBetweenNode1) {
8901               nbInserted = theNodesToInsert.size();
8902
8903               // add nodes to insert in reversed order
8904               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8905               nIt--;
8906               for (; nIt != theNodesToInsert.begin(); nIt--) {
8907                 poly_nodes.push_back(*nIt);
8908               }
8909               poly_nodes.push_back(*nIt);
8910             }
8911           }
8912           else {
8913           }
8914         }
8915       }
8916       quantities[iface] = nbFaceNodes + nbInserted;
8917     }
8918
8919     // Replace or update the volume
8920     SMESHDS_Mesh *aMesh = GetMeshDS();
8921
8922     if (elem->IsPoly()) {
8923       aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8924
8925     }
8926     else {
8927       int aShapeId = FindShape( elem );
8928
8929       SMDS_MeshElement* newElem =
8930         aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8931       myLastCreatedElems.Append(newElem);
8932       if (aShapeId && newElem)
8933         aMesh->SetMeshElementOnShape(newElem, aShapeId);
8934
8935       aMesh->RemoveElement(elem);
8936     }
8937   }
8938 }
8939
8940 namespace
8941 {
8942   //================================================================================
8943   /*!
8944    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8945    */
8946   //================================================================================
8947
8948   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8949                            vector<const SMDS_MeshNode *> & nodes,
8950                            vector<int> &                   nbNodeInFaces )
8951   {
8952     nodes.clear();
8953     nbNodeInFaces.clear();
8954     SMDS_VolumeTool vTool ( elem );
8955     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8956     {
8957       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8958       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8959       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8960     }
8961   }
8962 }
8963
8964 //=======================================================================
8965 /*!
8966  * \brief Convert elements contained in a submesh to quadratic
8967  * \return int - nb of checked elements
8968  */
8969 //=======================================================================
8970
8971 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8972                                              SMESH_MesherHelper& theHelper,
8973                                              const bool          theForce3d)
8974 {
8975   int nbElem = 0;
8976   if( !theSm ) return nbElem;
8977
8978   vector<int> nbNodeInFaces;
8979   vector<const SMDS_MeshNode *> nodes;
8980   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8981   while(ElemItr->more())
8982   {
8983     nbElem++;
8984     const SMDS_MeshElement* elem = ElemItr->next();
8985     if( !elem ) continue;
8986
8987     // analyse a necessity of conversion
8988     const SMDSAbs_ElementType aType = elem->GetType();
8989     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8990       continue;
8991     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8992     bool hasCentralNodes = false;
8993     if ( elem->IsQuadratic() )
8994     {
8995       bool alreadyOK;
8996       switch ( aGeomType ) {
8997       case SMDSEntity_Quad_Triangle:
8998       case SMDSEntity_Quad_Quadrangle:
8999       case SMDSEntity_Quad_Hexa:
9000         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9001
9002       case SMDSEntity_BiQuad_Triangle:
9003       case SMDSEntity_BiQuad_Quadrangle:
9004       case SMDSEntity_TriQuad_Hexa:
9005         alreadyOK = theHelper.GetIsBiQuadratic();
9006         hasCentralNodes = true;
9007         break;
9008       default:
9009         alreadyOK = true;
9010       }
9011       // take into account already present modium nodes
9012       switch ( aType ) {
9013       case SMDSAbs_Volume:
9014         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9015       case SMDSAbs_Face:
9016         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9017       case SMDSAbs_Edge:
9018         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9019       default:;
9020       }
9021       if ( alreadyOK )
9022         continue;
9023     }
9024     // get elem data needed to re-create it
9025     //
9026     const int id      = elem->GetID();
9027     const int nbNodes = elem->NbCornerNodes();
9028     nodes.assign(elem->begin_nodes(), elem->end_nodes());
9029     if ( aGeomType == SMDSEntity_Polyhedra )
9030       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9031     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9032       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9033
9034     // remove a linear element
9035     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9036
9037     // remove central nodes of biquadratic elements (biquad->quad convertion)
9038     if ( hasCentralNodes )
9039       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9040         if ( nodes[i]->NbInverseElements() == 0 )
9041           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9042
9043     const SMDS_MeshElement* NewElem = 0;
9044
9045     switch( aType )
9046     {
9047     case SMDSAbs_Edge :
9048       {
9049         NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9050         break;
9051       }
9052     case SMDSAbs_Face :
9053       {
9054         switch(nbNodes)
9055         {
9056         case 3:
9057           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9058           break;
9059         case 4:
9060           NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9061           break;
9062         default:
9063           NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9064         }
9065         break;
9066       }
9067     case SMDSAbs_Volume :
9068       {
9069         switch( aGeomType )
9070         {
9071         case SMDSEntity_Tetra:
9072           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9073           break;
9074         case SMDSEntity_Pyramid:
9075           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9076           break;
9077         case SMDSEntity_Penta:
9078           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9079           break;
9080         case SMDSEntity_Hexa:
9081         case SMDSEntity_Quad_Hexa:
9082         case SMDSEntity_TriQuad_Hexa:
9083           NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9084                                         nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9085           break;
9086         case SMDSEntity_Hexagonal_Prism:
9087         default:
9088           NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9089         }
9090         break;
9091       }
9092     default :
9093       continue;
9094     }
9095     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9096     if( NewElem && NewElem->getshapeId() < 1 )
9097       theSm->AddElement( NewElem );
9098   }
9099   return nbElem;
9100 }
9101 //=======================================================================
9102 //function : ConvertToQuadratic
9103 //purpose  :
9104 //=======================================================================
9105
9106 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9107 {
9108   SMESHDS_Mesh* meshDS = GetMeshDS();
9109
9110   SMESH_MesherHelper aHelper(*myMesh);
9111
9112   aHelper.SetIsQuadratic( true );
9113   aHelper.SetIsBiQuadratic( theToBiQuad );
9114   aHelper.SetElementsOnShape(true);
9115   aHelper.ToFixNodeParameters( true );
9116
9117   // convert elements assigned to sub-meshes
9118   int nbCheckedElems = 0;
9119   if ( myMesh->HasShapeToMesh() )
9120   {
9121     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9122     {
9123       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9124       while ( smIt->more() ) {
9125         SMESH_subMesh* sm = smIt->next();
9126         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9127           aHelper.SetSubShape( sm->GetSubShape() );
9128           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9129         }
9130       }
9131     }
9132   }
9133
9134   // convert elements NOT assigned to sub-meshes
9135   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9136   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9137   {
9138     aHelper.SetElementsOnShape(false);
9139     SMESHDS_SubMesh *smDS = 0;
9140
9141     // convert edges
9142     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9143     while( aEdgeItr->more() )
9144     {
9145       const SMDS_MeshEdge* edge = aEdgeItr->next();
9146       if ( !edge->IsQuadratic() )
9147       {
9148         int                  id = edge->GetID();
9149         const SMDS_MeshNode* n1 = edge->GetNode(0);
9150         const SMDS_MeshNode* n2 = edge->GetNode(1);
9151
9152         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9153
9154         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9155         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9156       }
9157       else
9158       {
9159         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9160       }
9161     }
9162
9163     // convert faces
9164     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9165     while( aFaceItr->more() )
9166     {
9167       const SMDS_MeshFace* face = aFaceItr->next();
9168       if ( !face ) continue;
9169       
9170       const SMDSAbs_EntityType type = face->GetEntityType();
9171       bool alreadyOK;
9172       switch( type )
9173       {
9174       case SMDSEntity_Quad_Triangle:
9175       case SMDSEntity_Quad_Quadrangle:
9176         alreadyOK = !theToBiQuad;
9177         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9178         break;
9179       case SMDSEntity_BiQuad_Triangle:
9180       case SMDSEntity_BiQuad_Quadrangle:
9181         alreadyOK = theToBiQuad;
9182         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9183         break;
9184       default: alreadyOK = false;
9185       }
9186       if ( alreadyOK )
9187         continue;
9188
9189       const int id = face->GetID();
9190       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9191
9192       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9193
9194       SMDS_MeshFace * NewFace = 0;
9195       switch( type )
9196       {
9197       case SMDSEntity_Triangle:
9198       case SMDSEntity_Quad_Triangle:
9199       case SMDSEntity_BiQuad_Triangle:
9200         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9201         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9202           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9203         break;
9204
9205       case SMDSEntity_Quadrangle:
9206       case SMDSEntity_Quad_Quadrangle:
9207       case SMDSEntity_BiQuad_Quadrangle:
9208         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9209         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9210           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9211         break;
9212
9213       default:;
9214         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9215       }
9216       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9217     }
9218
9219     // convert volumes
9220     vector<int> nbNodeInFaces;
9221     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9222     while(aVolumeItr->more())
9223     {
9224       const SMDS_MeshVolume* volume = aVolumeItr->next();
9225       if ( !volume ) continue;
9226
9227       const SMDSAbs_EntityType type = volume->GetEntityType();
9228       if ( volume->IsQuadratic() )
9229       {
9230         bool alreadyOK;
9231         switch ( type )
9232         {
9233         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9234         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9235         default:                      alreadyOK = true;
9236         }
9237         if ( alreadyOK )
9238         {
9239           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9240           continue;
9241         }
9242       }
9243       const int id = volume->GetID();
9244       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9245       if ( type == SMDSEntity_Polyhedra )
9246         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9247       else if ( type == SMDSEntity_Hexagonal_Prism )
9248         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9249
9250       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9251
9252       SMDS_MeshVolume * NewVolume = 0;
9253       switch ( type )
9254       {
9255       case SMDSEntity_Tetra:
9256         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9257         break;
9258       case SMDSEntity_Hexa:
9259       case SMDSEntity_Quad_Hexa:
9260       case SMDSEntity_TriQuad_Hexa:
9261         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9262                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9263         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9264           if ( nodes[i]->NbInverseElements() == 0 )
9265             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9266         break;
9267       case SMDSEntity_Pyramid:
9268         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9269                                       nodes[3], nodes[4], id, theForce3d);
9270         break;
9271       case SMDSEntity_Penta:
9272         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9273                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9274         break;
9275       case SMDSEntity_Hexagonal_Prism:
9276       default:
9277         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9278       }
9279       ReplaceElemInGroups(volume, NewVolume, meshDS);
9280     }
9281   }
9282
9283   if ( !theForce3d )
9284   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9285     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9286     // aHelper.FixQuadraticElements(myError);
9287     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9288   }
9289 }
9290
9291 //================================================================================
9292 /*!
9293  * \brief Makes given elements quadratic
9294  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9295  *  \param theElements - elements to make quadratic
9296  */
9297 //================================================================================
9298
9299 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9300                                           TIDSortedElemSet& theElements,
9301                                           const bool        theToBiQuad)
9302 {
9303   if ( theElements.empty() ) return;
9304
9305   // we believe that all theElements are of the same type
9306   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9307
9308   // get all nodes shared by theElements
9309   TIDSortedNodeSet allNodes;
9310   TIDSortedElemSet::iterator eIt = theElements.begin();
9311   for ( ; eIt != theElements.end(); ++eIt )
9312     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9313
9314   // complete theElements with elements of lower dim whose all nodes are in allNodes
9315
9316   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9317   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9318   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9319   for ( ; nIt != allNodes.end(); ++nIt )
9320   {
9321     const SMDS_MeshNode* n = *nIt;
9322     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9323     while ( invIt->more() )
9324     {
9325       const SMDS_MeshElement*      e = invIt->next();
9326       const SMDSAbs_ElementType type = e->GetType();
9327       if ( e->IsQuadratic() )
9328       {
9329         quadAdjacentElems[ type ].insert( e );
9330
9331         bool alreadyOK;
9332         switch ( e->GetEntityType() ) {
9333         case SMDSEntity_Quad_Triangle:
9334         case SMDSEntity_Quad_Quadrangle:
9335         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9336         case SMDSEntity_BiQuad_Triangle:
9337         case SMDSEntity_BiQuad_Quadrangle:
9338         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9339         default:                           alreadyOK = true;
9340         }
9341         if ( alreadyOK )
9342           continue;
9343       }
9344       if ( type >= elemType )
9345         continue; // same type or more complex linear element
9346
9347       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9348         continue; // e is already checked
9349
9350       // check nodes
9351       bool allIn = true;
9352       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9353       while ( nodeIt->more() && allIn )
9354         allIn = allNodes.count( nodeIt->next() );
9355       if ( allIn )
9356         theElements.insert(e );
9357     }
9358   }
9359
9360   SMESH_MesherHelper helper(*myMesh);
9361   helper.SetIsQuadratic( true );
9362   helper.SetIsBiQuadratic( theToBiQuad );
9363
9364   // add links of quadratic adjacent elements to the helper
9365
9366   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9367     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9368           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9369     {
9370       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9371     }
9372   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9373     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9374           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9375     {
9376       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9377     }
9378   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9379     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9380           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9381     {
9382       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9383     }
9384
9385   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9386
9387   SMESHDS_Mesh*  meshDS = GetMeshDS();
9388   SMESHDS_SubMesh* smDS = 0;
9389   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9390   {
9391     const SMDS_MeshElement* elem = *eIt;
9392
9393     bool alreadyOK;
9394     int nbCentralNodes = 0;
9395     switch ( elem->GetEntityType() ) {
9396       // linear convertible
9397     case SMDSEntity_Edge:
9398     case SMDSEntity_Triangle:
9399     case SMDSEntity_Quadrangle:
9400     case SMDSEntity_Tetra:
9401     case SMDSEntity_Pyramid:
9402     case SMDSEntity_Hexa:
9403     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9404       // quadratic that can become bi-quadratic
9405     case SMDSEntity_Quad_Triangle:
9406     case SMDSEntity_Quad_Quadrangle:
9407     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9408       // bi-quadratic
9409     case SMDSEntity_BiQuad_Triangle:
9410     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9411     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9412       // the rest
9413     default:                           alreadyOK = true;
9414     }
9415     if ( alreadyOK ) continue;
9416
9417     const SMDSAbs_ElementType type = elem->GetType();
9418     const int                   id = elem->GetID();
9419     const int              nbNodes = elem->NbCornerNodes();
9420     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9421
9422     helper.SetSubShape( elem->getshapeId() );
9423
9424     if ( !smDS || !smDS->Contains( elem ))
9425       smDS = meshDS->MeshElements( elem->getshapeId() );
9426     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9427
9428     SMDS_MeshElement * newElem = 0;
9429     switch( nbNodes )
9430     {
9431     case 4: // cases for most frequently used element types go first (for optimization)
9432       if ( type == SMDSAbs_Volume )
9433         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9434       else
9435         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9436       break;
9437     case 8:
9438       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9439                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9440       break;
9441     case 3:
9442       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9443       break;
9444     case 2:
9445       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9446       break;
9447     case 5:
9448       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9449                                  nodes[4], id, theForce3d);
9450       break;
9451     case 6:
9452       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9453                                  nodes[4], nodes[5], id, theForce3d);
9454       break;
9455     default:;
9456     }
9457     ReplaceElemInGroups( elem, newElem, meshDS);
9458     if( newElem && smDS )
9459       smDS->AddElement( newElem );
9460
9461      // remove central nodes
9462     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9463       if ( nodes[i]->NbInverseElements() == 0 )
9464         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9465
9466   } // loop on theElements
9467
9468   if ( !theForce3d )
9469   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9470     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9471     // helper.FixQuadraticElements( myError );
9472     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9473   }
9474 }
9475
9476 //=======================================================================
9477 /*!
9478  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9479  * \return int - nb of checked elements
9480  */
9481 //=======================================================================
9482
9483 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9484                                      SMDS_ElemIteratorPtr theItr,
9485                                      const int            theShapeID)
9486 {
9487   int nbElem = 0;
9488   SMESHDS_Mesh* meshDS = GetMeshDS();
9489
9490   while( theItr->more() )
9491   {
9492     const SMDS_MeshElement* elem = theItr->next();
9493     nbElem++;
9494     if( elem && elem->IsQuadratic())
9495     {
9496       int id                    = elem->GetID();
9497       int nbCornerNodes         = elem->NbCornerNodes();
9498       SMDSAbs_ElementType aType = elem->GetType();
9499
9500       vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9501
9502       //remove a quadratic element
9503       if ( !theSm || !theSm->Contains( elem ))
9504         theSm = meshDS->MeshElements( elem->getshapeId() );
9505       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9506
9507       // remove medium nodes
9508       for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9509         if ( nodes[i]->NbInverseElements() == 0 )
9510           meshDS->RemoveFreeNode( nodes[i], theSm );
9511
9512       // add a linear element
9513       nodes.resize( nbCornerNodes );
9514       SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9515       ReplaceElemInGroups(elem, newElem, meshDS);
9516       if( theSm && newElem )
9517         theSm->AddElement( newElem );
9518     }
9519   }
9520   return nbElem;
9521 }
9522
9523 //=======================================================================
9524 //function : ConvertFromQuadratic
9525 //purpose  :
9526 //=======================================================================
9527
9528 bool SMESH_MeshEditor::ConvertFromQuadratic()
9529 {
9530   int nbCheckedElems = 0;
9531   if ( myMesh->HasShapeToMesh() )
9532   {
9533     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9534     {
9535       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9536       while ( smIt->more() ) {
9537         SMESH_subMesh* sm = smIt->next();
9538         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9539           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9540       }
9541     }
9542   }
9543
9544   int totalNbElems =
9545     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9546   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9547   {
9548     SMESHDS_SubMesh *aSM = 0;
9549     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9550   }
9551
9552   return true;
9553 }
9554
9555 namespace
9556 {
9557   //================================================================================
9558   /*!
9559    * \brief Return true if all medium nodes of the element are in the node set
9560    */
9561   //================================================================================
9562
9563   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9564   {
9565     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9566       if ( !nodeSet.count( elem->GetNode(i) ))
9567         return false;
9568     return true;
9569   }
9570 }
9571
9572 //================================================================================
9573 /*!
9574  * \brief Makes given elements linear
9575  */
9576 //================================================================================
9577
9578 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9579 {
9580   if ( theElements.empty() ) return;
9581
9582   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9583   set<int> mediumNodeIDs;
9584   TIDSortedElemSet::iterator eIt = theElements.begin();
9585   for ( ; eIt != theElements.end(); ++eIt )
9586   {
9587     const SMDS_MeshElement* e = *eIt;
9588     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9589       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9590   }
9591
9592   // replace given elements by linear ones
9593   SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9594   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9595
9596   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9597   // except those elements sharing medium nodes of quadratic element whose medium nodes
9598   // are not all in mediumNodeIDs
9599
9600   // get remaining medium nodes
9601   TIDSortedNodeSet mediumNodes;
9602   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9603   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9604     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9605       mediumNodes.insert( mediumNodes.end(), n );
9606
9607   // find more quadratic elements to convert
9608   TIDSortedElemSet moreElemsToConvert;
9609   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9610   for ( ; nIt != mediumNodes.end(); ++nIt )
9611   {
9612     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9613     while ( invIt->more() )
9614     {
9615       const SMDS_MeshElement* e = invIt->next();
9616       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9617       {
9618         // find a more complex element including e and
9619         // whose medium nodes are not in mediumNodes
9620         bool complexFound = false;
9621         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9622         {
9623           SMDS_ElemIteratorPtr invIt2 =
9624             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9625           while ( invIt2->more() )
9626           {
9627             const SMDS_MeshElement* eComplex = invIt2->next();
9628             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9629             {
9630               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9631               if ( nbCommonNodes == e->NbNodes())
9632               {
9633                 complexFound = true;
9634                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9635                 break;
9636               }
9637             }
9638           }
9639         }
9640         if ( !complexFound )
9641           moreElemsToConvert.insert( e );
9642       }
9643     }
9644   }
9645   elemIt = elemSetIterator( moreElemsToConvert );
9646   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9647 }
9648
9649 //=======================================================================
9650 //function : SewSideElements
9651 //purpose  :
9652 //=======================================================================
9653
9654 SMESH_MeshEditor::Sew_Error
9655 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9656                                    TIDSortedElemSet&    theSide2,
9657                                    const SMDS_MeshNode* theFirstNode1,
9658                                    const SMDS_MeshNode* theFirstNode2,
9659                                    const SMDS_MeshNode* theSecondNode1,
9660                                    const SMDS_MeshNode* theSecondNode2)
9661 {
9662   myLastCreatedElems.Clear();
9663   myLastCreatedNodes.Clear();
9664
9665   MESSAGE ("::::SewSideElements()");
9666   if ( theSide1.size() != theSide2.size() )
9667     return SEW_DIFF_NB_OF_ELEMENTS;
9668
9669   Sew_Error aResult = SEW_OK;
9670   // Algo:
9671   // 1. Build set of faces representing each side
9672   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9673   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9674
9675   // =======================================================================
9676   // 1. Build set of faces representing each side:
9677   // =======================================================================
9678   // a. build set of nodes belonging to faces
9679   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9680   // c. create temporary faces representing side of volumes if correspondent
9681   //    face does not exist
9682
9683   SMESHDS_Mesh* aMesh = GetMeshDS();
9684   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9685   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9686   TIDSortedElemSet             faceSet1, faceSet2;
9687   set<const SMDS_MeshElement*> volSet1,  volSet2;
9688   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9689   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9690   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9691   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9692   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9693   int iSide, iFace, iNode;
9694
9695   list<const SMDS_MeshElement* > tempFaceList;
9696   for ( iSide = 0; iSide < 2; iSide++ ) {
9697     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9698     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9699     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9700     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9701     set<const SMDS_MeshElement*>::iterator vIt;
9702     TIDSortedElemSet::iterator eIt;
9703     set<const SMDS_MeshNode*>::iterator    nIt;
9704
9705     // check that given nodes belong to given elements
9706     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9707     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9708     int firstIndex = -1, secondIndex = -1;
9709     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9710       const SMDS_MeshElement* elem = *eIt;
9711       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9712       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9713       if ( firstIndex > -1 && secondIndex > -1 ) break;
9714     }
9715     if ( firstIndex < 0 || secondIndex < 0 ) {
9716       // we can simply return until temporary faces created
9717       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9718     }
9719
9720     // -----------------------------------------------------------
9721     // 1a. Collect nodes of existing faces
9722     //     and build set of face nodes in order to detect missing
9723     //     faces corresponding to sides of volumes
9724     // -----------------------------------------------------------
9725
9726     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9727
9728     // loop on the given element of a side
9729     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9730       //const SMDS_MeshElement* elem = *eIt;
9731       const SMDS_MeshElement* elem = *eIt;
9732       if ( elem->GetType() == SMDSAbs_Face ) {
9733         faceSet->insert( elem );
9734         set <const SMDS_MeshNode*> faceNodeSet;
9735         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9736         while ( nodeIt->more() ) {
9737           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9738           nodeSet->insert( n );
9739           faceNodeSet.insert( n );
9740         }
9741         setOfFaceNodeSet.insert( faceNodeSet );
9742       }
9743       else if ( elem->GetType() == SMDSAbs_Volume )
9744         volSet->insert( elem );
9745     }
9746     // ------------------------------------------------------------------------------
9747     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9748     // ------------------------------------------------------------------------------
9749
9750     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9751       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9752       while ( fIt->more() ) { // loop on faces sharing a node
9753         const SMDS_MeshElement* f = fIt->next();
9754         if ( faceSet->find( f ) == faceSet->end() ) {
9755           // check if all nodes are in nodeSet and
9756           // complete setOfFaceNodeSet if they are
9757           set <const SMDS_MeshNode*> faceNodeSet;
9758           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9759           bool allInSet = true;
9760           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9761             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9762             if ( nodeSet->find( n ) == nodeSet->end() )
9763               allInSet = false;
9764             else
9765               faceNodeSet.insert( n );
9766           }
9767           if ( allInSet ) {
9768             faceSet->insert( f );
9769             setOfFaceNodeSet.insert( faceNodeSet );
9770           }
9771         }
9772       }
9773     }
9774
9775     // -------------------------------------------------------------------------
9776     // 1c. Create temporary faces representing sides of volumes if correspondent
9777     //     face does not exist
9778     // -------------------------------------------------------------------------
9779
9780     if ( !volSet->empty() ) {
9781       //int nodeSetSize = nodeSet->size();
9782
9783       // loop on given volumes
9784       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9785         SMDS_VolumeTool vol (*vIt);
9786         // loop on volume faces: find free faces
9787         // --------------------------------------
9788         list<const SMDS_MeshElement* > freeFaceList;
9789         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9790           if ( !vol.IsFreeFace( iFace ))
9791             continue;
9792           // check if there is already a face with same nodes in a face set
9793           const SMDS_MeshElement* aFreeFace = 0;
9794           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9795           int nbNodes = vol.NbFaceNodes( iFace );
9796           set <const SMDS_MeshNode*> faceNodeSet;
9797           vol.GetFaceNodes( iFace, faceNodeSet );
9798           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9799           if ( isNewFace ) {
9800             // no such a face is given but it still can exist, check it
9801             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9802             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9803           }
9804           if ( !aFreeFace ) {
9805             // create a temporary face
9806             if ( nbNodes == 3 ) {
9807               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9808               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9809             }
9810             else if ( nbNodes == 4 ) {
9811               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9812               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9813             }
9814             else {
9815               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9816               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9817               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9818             }
9819             if ( aFreeFace )
9820               tempFaceList.push_back( aFreeFace );
9821           }
9822
9823           if ( aFreeFace )
9824             freeFaceList.push_back( aFreeFace );
9825
9826         } // loop on faces of a volume
9827
9828         // choose one of several free faces of a volume
9829         // --------------------------------------------
9830         if ( freeFaceList.size() > 1 ) {
9831           // choose a face having max nb of nodes shared by other elems of a side
9832           int maxNbNodes = -1;
9833           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9834           while ( fIt != freeFaceList.end() ) { // loop on free faces
9835             int nbSharedNodes = 0;
9836             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9837             while ( nodeIt->more() ) { // loop on free face nodes
9838               const SMDS_MeshNode* n =
9839                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9840               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9841               while ( invElemIt->more() ) {
9842                 const SMDS_MeshElement* e = invElemIt->next();
9843                 nbSharedNodes += faceSet->count( e );
9844                 nbSharedNodes += elemSet->count( e );
9845               }
9846             }
9847             if ( nbSharedNodes > maxNbNodes ) {
9848               maxNbNodes = nbSharedNodes;
9849               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9850             }
9851             else if ( nbSharedNodes == maxNbNodes ) {
9852               fIt++;
9853             }
9854             else {
9855               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9856             }
9857           }
9858           if ( freeFaceList.size() > 1 )
9859           {
9860             // could not choose one face, use another way
9861             // choose a face most close to the bary center of the opposite side
9862             gp_XYZ aBC( 0., 0., 0. );
9863             set <const SMDS_MeshNode*> addedNodes;
9864             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9865             eIt = elemSet2->begin();
9866             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9867               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9868               while ( nodeIt->more() ) { // loop on free face nodes
9869                 const SMDS_MeshNode* n =
9870                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9871                 if ( addedNodes.insert( n ).second )
9872                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9873               }
9874             }
9875             aBC /= addedNodes.size();
9876             double minDist = DBL_MAX;
9877             fIt = freeFaceList.begin();
9878             while ( fIt != freeFaceList.end() ) { // loop on free faces
9879               double dist = 0;
9880               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9881               while ( nodeIt->more() ) { // loop on free face nodes
9882                 const SMDS_MeshNode* n =
9883                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9884                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9885                 dist += ( aBC - p ).SquareModulus();
9886               }
9887               if ( dist < minDist ) {
9888                 minDist = dist;
9889                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9890               }
9891               else
9892                 fIt = freeFaceList.erase( fIt++ );
9893             }
9894           }
9895         } // choose one of several free faces of a volume
9896
9897         if ( freeFaceList.size() == 1 ) {
9898           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9899           faceSet->insert( aFreeFace );
9900           // complete a node set with nodes of a found free face
9901           //           for ( iNode = 0; iNode < ; iNode++ )
9902           //             nodeSet->insert( fNodes[ iNode ] );
9903         }
9904
9905       } // loop on volumes of a side
9906
9907       //       // complete a set of faces if new nodes in a nodeSet appeared
9908       //       // ----------------------------------------------------------
9909       //       if ( nodeSetSize != nodeSet->size() ) {
9910       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9911       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9912       //           while ( fIt->more() ) { // loop on faces sharing a node
9913       //             const SMDS_MeshElement* f = fIt->next();
9914       //             if ( faceSet->find( f ) == faceSet->end() ) {
9915       //               // check if all nodes are in nodeSet and
9916       //               // complete setOfFaceNodeSet if they are
9917       //               set <const SMDS_MeshNode*> faceNodeSet;
9918       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9919       //               bool allInSet = true;
9920       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9921       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9922       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9923       //                   allInSet = false;
9924       //                 else
9925       //                   faceNodeSet.insert( n );
9926       //               }
9927       //               if ( allInSet ) {
9928       //                 faceSet->insert( f );
9929       //                 setOfFaceNodeSet.insert( faceNodeSet );
9930       //               }
9931       //             }
9932       //           }
9933       //         }
9934       //       }
9935     } // Create temporary faces, if there are volumes given
9936   } // loop on sides
9937
9938   if ( faceSet1.size() != faceSet2.size() ) {
9939     // delete temporary faces: they are in reverseElements of actual nodes
9940 //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9941 //    while ( tmpFaceIt->more() )
9942 //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9943 //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9944 //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9945 //      aMesh->RemoveElement(*tmpFaceIt);
9946     MESSAGE("Diff nb of faces");
9947     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9948   }
9949
9950   // ============================================================
9951   // 2. Find nodes to merge:
9952   //              bind a node to remove to a node to put instead
9953   // ============================================================
9954
9955   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9956   if ( theFirstNode1 != theFirstNode2 )
9957     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9958   if ( theSecondNode1 != theSecondNode2 )
9959     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9960
9961   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9962   set< long > linkIdSet; // links to process
9963   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9964
9965   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9966   list< NLink > linkList[2];
9967   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9968   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9969   // loop on links in linkList; find faces by links and append links
9970   // of the found faces to linkList
9971   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9972   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9973   {
9974     NLink link[] = { *linkIt[0], *linkIt[1] };
9975     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9976     if ( !linkIdSet.count( linkID ) )
9977       continue;
9978
9979     // by links, find faces in the face sets,
9980     // and find indices of link nodes in the found faces;
9981     // in a face set, there is only one or no face sharing a link
9982     // ---------------------------------------------------------------
9983
9984     const SMDS_MeshElement* face[] = { 0, 0 };
9985     vector<const SMDS_MeshNode*> fnodes[2];
9986     int iLinkNode[2][2];
9987     TIDSortedElemSet avoidSet;
9988     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9989       const SMDS_MeshNode* n1 = link[iSide].first;
9990       const SMDS_MeshNode* n2 = link[iSide].second;
9991       //cout << "Side " << iSide << " ";
9992       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9993       // find a face by two link nodes
9994       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9995                                                       *faceSetPtr[ iSide ], avoidSet,
9996                                                       &iLinkNode[iSide][0],
9997                                                       &iLinkNode[iSide][1] );
9998       if ( face[ iSide ])
9999       {
10000         //cout << " F " << face[ iSide]->GetID() <<endl;
10001         faceSetPtr[ iSide ]->erase( face[ iSide ]);
10002         // put face nodes to fnodes
10003         if ( face[ iSide ]->IsQuadratic() )
10004         {
10005           // use interlaced nodes iterator
10006           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10007           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10008           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10009           while ( nIter->more() )
10010             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10011         }
10012         else
10013         {
10014           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10015                                   face[ iSide ]->end_nodes() );
10016         }
10017         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10018       }
10019     }
10020
10021     // check similarity of elements of the sides
10022     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10023       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10024       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10025         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10026       }
10027       else {
10028         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10029       }
10030       break; // do not return because it's necessary to remove tmp faces
10031     }
10032
10033     // set nodes to merge
10034     // -------------------
10035
10036     if ( face[0] && face[1] )  {
10037       const int nbNodes = face[0]->NbNodes();
10038       if ( nbNodes != face[1]->NbNodes() ) {
10039         MESSAGE("Diff nb of face nodes");
10040         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10041         break; // do not return because it s necessary to remove tmp faces
10042       }
10043       bool reverse[] = { false, false }; // order of nodes in the link
10044       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10045         // analyse link orientation in faces
10046         int i1 = iLinkNode[ iSide ][ 0 ];
10047         int i2 = iLinkNode[ iSide ][ 1 ];
10048         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10049       }
10050       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10051       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10052       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10053       {
10054         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10055                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10056       }
10057
10058       // add other links of the faces to linkList
10059       // -----------------------------------------
10060
10061       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10062         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10063         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10064         if ( !iter_isnew.second ) { // already in a set: no need to process
10065           linkIdSet.erase( iter_isnew.first );
10066         }
10067         else // new in set == encountered for the first time: add
10068         {
10069           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10070           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10071           linkList[0].push_back ( NLink( n1, n2 ));
10072           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10073         }
10074       }
10075     } // 2 faces found
10076
10077     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10078       break;
10079
10080   } // loop on link lists
10081
10082   if ( aResult == SEW_OK &&
10083        ( //linkIt[0] != linkList[0].end() ||
10084          !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10085     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10086              " " << (faceSetPtr[1]->empty()));
10087     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10088   }
10089
10090   // ====================================================================
10091   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10092   // ====================================================================
10093
10094   // delete temporary faces
10095 //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10096 //  while ( tmpFaceIt->more() )
10097 //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10098   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10099   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10100     aMesh->RemoveElement(*tmpFaceIt);
10101
10102   if ( aResult != SEW_OK)
10103     return aResult;
10104
10105   list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10106   // loop on nodes replacement map
10107   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10108   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10109     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10110       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10111       nodeIDsToRemove.push_back( nToRemove->GetID() );
10112       // loop on elements sharing nToRemove
10113       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10114       while ( invElemIt->more() ) {
10115         const SMDS_MeshElement* e = invElemIt->next();
10116         // get a new suite of nodes: make replacement
10117         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10118         vector< const SMDS_MeshNode*> nodes( nbNodes );
10119         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10120         while ( nIt->more() ) {
10121           const SMDS_MeshNode* n =
10122             static_cast<const SMDS_MeshNode*>( nIt->next() );
10123           nnIt = nReplaceMap.find( n );
10124           if ( nnIt != nReplaceMap.end() ) {
10125             nbReplaced++;
10126             n = (*nnIt).second;
10127           }
10128           nodes[ i++ ] = n;
10129         }
10130         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10131         //         elemIDsToRemove.push_back( e->GetID() );
10132         //       else
10133         if ( nbReplaced )
10134           {
10135             SMDSAbs_ElementType etyp = e->GetType();
10136             SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10137             if (newElem)
10138               {
10139                 myLastCreatedElems.Append(newElem);
10140                 AddToSameGroups(newElem, e, aMesh);
10141                 int aShapeId = e->getshapeId();
10142                 if ( aShapeId )
10143                   {
10144                     aMesh->SetMeshElementOnShape( newElem, aShapeId );
10145                   }
10146               }
10147             aMesh->RemoveElement(e);
10148           }
10149       }
10150     }
10151
10152   Remove( nodeIDsToRemove, true );
10153
10154   return aResult;
10155 }
10156
10157 //================================================================================
10158 /*!
10159  * \brief Find corresponding nodes in two sets of faces
10160  * \param theSide1 - first face set
10161  * \param theSide2 - second first face
10162  * \param theFirstNode1 - a boundary node of set 1
10163  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10164  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10165  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10166  * \param nReplaceMap - output map of corresponding nodes
10167  * \return bool  - is a success or not
10168  */
10169 //================================================================================
10170
10171 #ifdef _DEBUG_
10172 //#define DEBUG_MATCHING_NODES
10173 #endif
10174
10175 SMESH_MeshEditor::Sew_Error
10176 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10177                                     set<const SMDS_MeshElement*>& theSide2,
10178                                     const SMDS_MeshNode*          theFirstNode1,
10179                                     const SMDS_MeshNode*          theFirstNode2,
10180                                     const SMDS_MeshNode*          theSecondNode1,
10181                                     const SMDS_MeshNode*          theSecondNode2,
10182                                     TNodeNodeMap &                nReplaceMap)
10183 {
10184   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10185
10186   nReplaceMap.clear();
10187   if ( theFirstNode1 != theFirstNode2 )
10188     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10189   if ( theSecondNode1 != theSecondNode2 )
10190     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10191
10192   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10193   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10194
10195   list< NLink > linkList[2];
10196   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10197   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10198
10199   // loop on links in linkList; find faces by links and append links
10200   // of the found faces to linkList
10201   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10202   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10203     NLink link[] = { *linkIt[0], *linkIt[1] };
10204     if ( linkSet.find( link[0] ) == linkSet.end() )
10205       continue;
10206
10207     // by links, find faces in the face sets,
10208     // and find indices of link nodes in the found faces;
10209     // in a face set, there is only one or no face sharing a link
10210     // ---------------------------------------------------------------
10211
10212     const SMDS_MeshElement* face[] = { 0, 0 };
10213     list<const SMDS_MeshNode*> notLinkNodes[2];
10214     //bool reverse[] = { false, false }; // order of notLinkNodes
10215     int nbNodes[2];
10216     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10217     {
10218       const SMDS_MeshNode* n1 = link[iSide].first;
10219       const SMDS_MeshNode* n2 = link[iSide].second;
10220       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10221       set< const SMDS_MeshElement* > facesOfNode1;
10222       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10223       {
10224         // during a loop of the first node, we find all faces around n1,
10225         // during a loop of the second node, we find one face sharing both n1 and n2
10226         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10227         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10228         while ( fIt->more() ) { // loop on faces sharing a node
10229           const SMDS_MeshElement* f = fIt->next();
10230           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10231               ! facesOfNode1.insert( f ).second ) // f encounters twice
10232           {
10233             if ( face[ iSide ] ) {
10234               MESSAGE( "2 faces per link " );
10235               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10236             }
10237             face[ iSide ] = f;
10238             faceSet->erase( f );
10239
10240             // get not link nodes
10241             int nbN = f->NbNodes();
10242             if ( f->IsQuadratic() )
10243               nbN /= 2;
10244             nbNodes[ iSide ] = nbN;
10245             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10246             int i1 = f->GetNodeIndex( n1 );
10247             int i2 = f->GetNodeIndex( n2 );
10248             int iEnd = nbN, iBeg = -1, iDelta = 1;
10249             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10250             if ( reverse ) {
10251               std::swap( iEnd, iBeg ); iDelta = -1;
10252             }
10253             int i = i2;
10254             while ( true ) {
10255               i += iDelta;
10256               if ( i == iEnd ) i = iBeg + iDelta;
10257               if ( i == i1 ) break;
10258               nodes.push_back ( f->GetNode( i ) );
10259             }
10260           }
10261         }
10262       }
10263     }
10264     // check similarity of elements of the sides
10265     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10266       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10267       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10268         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10269       }
10270       else {
10271         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10272       }
10273     }
10274
10275     // set nodes to merge
10276     // -------------------
10277
10278     if ( face[0] && face[1] )  {
10279       if ( nbNodes[0] != nbNodes[1] ) {
10280         MESSAGE("Diff nb of face nodes");
10281         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10282       }
10283 #ifdef DEBUG_MATCHING_NODES
10284       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10285                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10286                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10287 #endif
10288       int nbN = nbNodes[0];
10289       {
10290         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10291         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10292         for ( int i = 0 ; i < nbN - 2; ++i ) {
10293 #ifdef DEBUG_MATCHING_NODES
10294           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10295 #endif
10296           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10297         }
10298       }
10299
10300       // add other links of the face 1 to linkList
10301       // -----------------------------------------
10302
10303       const SMDS_MeshElement* f0 = face[0];
10304       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10305       for ( int i = 0; i < nbN; i++ )
10306       {
10307         const SMDS_MeshNode* n2 = f0->GetNode( i );
10308         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10309           linkSet.insert( SMESH_TLink( n1, n2 ));
10310         if ( !iter_isnew.second ) { // already in a set: no need to process
10311           linkSet.erase( iter_isnew.first );
10312         }
10313         else // new in set == encountered for the first time: add
10314         {
10315 #ifdef DEBUG_MATCHING_NODES
10316           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10317                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10318 #endif
10319           linkList[0].push_back ( NLink( n1, n2 ));
10320           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10321         }
10322         n1 = n2;
10323       }
10324     } // 2 faces found
10325   } // loop on link lists
10326
10327   return SEW_OK;
10328 }
10329
10330 //================================================================================
10331 /*!
10332  * \brief Create elements equal (on same nodes) to given ones
10333  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10334  *              elements of the uppest dimension are duplicated.
10335  */
10336 //================================================================================
10337
10338 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10339 {
10340   CrearLastCreated();
10341   SMESHDS_Mesh* mesh = GetMeshDS();
10342
10343   // get an element type and an iterator over elements
10344
10345   SMDSAbs_ElementType type;
10346   SMDS_ElemIteratorPtr elemIt;
10347   vector< const SMDS_MeshElement* > allElems;
10348   if ( theElements.empty() )
10349   {
10350     if ( mesh->NbNodes() == 0 )
10351       return;
10352     // get most complex type
10353     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10354       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10355       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10356     };
10357     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10358       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10359       {
10360         type = types[i];
10361         break;
10362       }
10363     // put all elements in the vector <allElems>
10364     allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10365     elemIt = mesh->elementsIterator( type );
10366     while ( elemIt->more() )
10367       allElems.push_back( elemIt->next());
10368     elemIt = elemSetIterator( allElems );
10369   }
10370   else
10371   {
10372     type = (*theElements.begin())->GetType();
10373     elemIt = elemSetIterator( theElements );
10374   }
10375
10376   // duplicate elements
10377
10378   if ( type == SMDSAbs_Ball )
10379   {
10380     SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10381     while ( elemIt->more() )
10382     {
10383       const SMDS_MeshElement* elem = elemIt->next();
10384       if ( elem->GetType() != SMDSAbs_Ball )
10385         continue;
10386       if (( elem = mesh->AddBall( elem->GetNode(0),
10387                                   vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10388         myLastCreatedElems.Append( elem );
10389     }
10390   }
10391   else
10392   {
10393     vector< const SMDS_MeshNode* > nodes;
10394     while ( elemIt->more() )
10395     {
10396       const SMDS_MeshElement* elem = elemIt->next();
10397       if ( elem->GetType() != type )
10398         continue;
10399
10400       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10401
10402       if ( type == SMDSAbs_Volume  && elem->GetVtkType() == VTK_POLYHEDRON )
10403       {
10404         std::vector<int> quantities =
10405           static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10406         elem = mesh->AddPolyhedralVolume( nodes, quantities );
10407       }
10408       else
10409       {
10410         AddElement( nodes, type, elem->IsPoly() );
10411         elem = 0; // myLastCreatedElems is already filled
10412       }
10413       if ( elem )
10414         myLastCreatedElems.Append( elem );
10415     }
10416   }
10417 }
10418
10419 //================================================================================
10420 /*!
10421   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10422   \param theElems - the list of elements (edges or faces) to be replicated
10423   The nodes for duplication could be found from these elements
10424   \param theNodesNot - list of nodes to NOT replicate
10425   \param theAffectedElems - the list of elements (cells and edges) to which the
10426   replicated nodes should be associated to.
10427   \return TRUE if operation has been completed successfully, FALSE otherwise
10428 */
10429 //================================================================================
10430
10431 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10432                                     const TIDSortedElemSet& theNodesNot,
10433                                     const TIDSortedElemSet& theAffectedElems )
10434 {
10435   myLastCreatedElems.Clear();
10436   myLastCreatedNodes.Clear();
10437
10438   if ( theElems.size() == 0 )
10439     return false;
10440
10441   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10442   if ( !aMeshDS )
10443     return false;
10444
10445   bool res = false;
10446   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10447   // duplicate elements and nodes
10448   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10449   // replce nodes by duplications
10450   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10451   return res;
10452 }
10453
10454 //================================================================================
10455 /*!
10456   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10457   \param theMeshDS - mesh instance
10458   \param theElems - the elements replicated or modified (nodes should be changed)
10459   \param theNodesNot - nodes to NOT replicate
10460   \param theNodeNodeMap - relation of old node to new created node
10461   \param theIsDoubleElem - flag os to replicate element or modify
10462   \return TRUE if operation has been completed successfully, FALSE otherwise
10463 */
10464 //================================================================================
10465
10466 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh*     theMeshDS,
10467                                     const TIDSortedElemSet& theElems,
10468                                     const TIDSortedElemSet& theNodesNot,
10469                                     std::map< const SMDS_MeshNode*,
10470                                     const SMDS_MeshNode* >& theNodeNodeMap,
10471                                     const bool theIsDoubleElem )
10472 {
10473   MESSAGE("doubleNodes");
10474   // iterate on through element and duplicate them (by nodes duplication)
10475   bool res = false;
10476   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10477   for ( ;  elemItr != theElems.end(); ++elemItr )
10478   {
10479     const SMDS_MeshElement* anElem = *elemItr;
10480     if (!anElem)
10481       continue;
10482
10483     bool isDuplicate = false;
10484     // duplicate nodes to duplicate element
10485     std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10486     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10487     int ind = 0;
10488     while ( anIter->more() )
10489     {
10490
10491       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10492       SMDS_MeshNode* aNewNode = aCurrNode;
10493       if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10494         aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10495       else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10496       {
10497         // duplicate node
10498         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10499         copyPosition( aCurrNode, aNewNode );
10500         theNodeNodeMap[ aCurrNode ] = aNewNode;
10501         myLastCreatedNodes.Append( aNewNode );
10502       }
10503       isDuplicate |= (aCurrNode != aNewNode);
10504       newNodes[ ind++ ] = aNewNode;
10505     }
10506     if ( !isDuplicate )
10507       continue;
10508
10509     if ( theIsDoubleElem )
10510       AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10511     else
10512       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10513
10514     res = true;
10515   }
10516   return res;
10517 }
10518
10519 //================================================================================
10520 /*!
10521   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10522   \param theNodes - identifiers of nodes to be doubled
10523   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10524   nodes. If list of element identifiers is empty then nodes are doubled but
10525   they not assigned to elements
10526   \return TRUE if operation has been completed successfully, FALSE otherwise
10527 */
10528 //================================================================================
10529
10530 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10531                                     const std::list< int >& theListOfModifiedElems )
10532 {
10533   MESSAGE("DoubleNodes");
10534   myLastCreatedElems.Clear();
10535   myLastCreatedNodes.Clear();
10536
10537   if ( theListOfNodes.size() == 0 )
10538     return false;
10539
10540   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10541   if ( !aMeshDS )
10542     return false;
10543
10544   // iterate through nodes and duplicate them
10545
10546   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10547
10548   std::list< int >::const_iterator aNodeIter;
10549   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10550   {
10551     int aCurr = *aNodeIter;
10552     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10553     if ( !aNode )
10554       continue;
10555
10556     // duplicate node
10557
10558     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10559     if ( aNewNode )
10560     {
10561       copyPosition( aNode, aNewNode );
10562       anOldNodeToNewNode[ aNode ] = aNewNode;
10563       myLastCreatedNodes.Append( aNewNode );
10564     }
10565   }
10566
10567   // Create map of new nodes for modified elements
10568
10569   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10570
10571   std::list< int >::const_iterator anElemIter;
10572   for ( anElemIter = theListOfModifiedElems.begin();
10573         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10574   {
10575     int aCurr = *anElemIter;
10576     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10577     if ( !anElem )
10578       continue;
10579
10580     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10581
10582     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10583     int ind = 0;
10584     while ( anIter->more() )
10585     {
10586       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10587       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10588       {
10589         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10590         aNodeArr[ ind++ ] = aNewNode;
10591       }
10592       else
10593         aNodeArr[ ind++ ] = aCurrNode;
10594     }
10595     anElemToNodes[ anElem ] = aNodeArr;
10596   }
10597
10598   // Change nodes of elements
10599
10600   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10601     anElemToNodesIter = anElemToNodes.begin();
10602   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10603   {
10604     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10605     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10606     if ( anElem )
10607       {
10608       MESSAGE("ChangeElementNodes");
10609       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10610       }
10611   }
10612
10613   return true;
10614 }
10615
10616 namespace {
10617
10618   //================================================================================
10619   /*!
10620   \brief Check if element located inside shape
10621   \return TRUE if IN or ON shape, FALSE otherwise
10622   */
10623   //================================================================================
10624
10625   template<class Classifier>
10626   bool isInside(const SMDS_MeshElement* theElem,
10627                 Classifier&             theClassifier,
10628                 const double            theTol)
10629   {
10630     gp_XYZ centerXYZ (0, 0, 0);
10631     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10632     while (aNodeItr->more())
10633       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10634
10635     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10636     theClassifier.Perform(aPnt, theTol);
10637     TopAbs_State aState = theClassifier.State();
10638     return (aState == TopAbs_IN || aState == TopAbs_ON );
10639   }
10640
10641   //================================================================================
10642   /*!
10643    * \brief Classifier of the 3D point on the TopoDS_Face
10644    *        with interaface suitable for isInside()
10645    */
10646   //================================================================================
10647
10648   struct _FaceClassifier
10649   {
10650     Extrema_ExtPS       _extremum;
10651     BRepAdaptor_Surface _surface;
10652     TopAbs_State        _state;
10653
10654     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10655     {
10656       _extremum.Initialize( _surface,
10657                             _surface.FirstUParameter(), _surface.LastUParameter(),
10658                             _surface.FirstVParameter(), _surface.LastVParameter(),
10659                             _surface.Tolerance(), _surface.Tolerance() );
10660     }
10661     void Perform(const gp_Pnt& aPnt, double theTol)
10662     {
10663       theTol *= theTol;
10664       _state = TopAbs_OUT;
10665       _extremum.Perform(aPnt);
10666       if ( _extremum.IsDone() )
10667         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10668           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10669     }
10670     TopAbs_State State() const
10671     {
10672       return _state;
10673     }
10674   };
10675 }
10676
10677 //================================================================================
10678 /*!
10679   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10680   This method is the first step of DoubleNodeElemGroupsInRegion.
10681   \param theElems - list of groups of elements (edges or faces) to be replicated
10682   \param theNodesNot - list of groups of nodes not to replicated
10683   \param theShape - shape to detect affected elements (element which geometric center
10684          located on or inside shape). If the shape is null, detection is done on faces orientations
10685          (select elements with a gravity center on the side given by faces normals).
10686          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10687          The replicated nodes should be associated to affected elements.
10688   \return groups of affected elements
10689   \sa DoubleNodeElemGroupsInRegion()
10690  */
10691 //================================================================================
10692
10693 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10694                                                    const TIDSortedElemSet& theNodesNot,
10695                                                    const TopoDS_Shape&     theShape,
10696                                                    TIDSortedElemSet&       theAffectedElems)
10697 {
10698   if ( theShape.IsNull() )
10699   {
10700     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10701     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10702     std::set<const SMDS_MeshElement*> edgesToCheck;
10703     alreadyCheckedNodes.clear();
10704     alreadyCheckedElems.clear();
10705     edgesToCheck.clear();
10706
10707     // --- iterates on elements to be replicated and get elements by back references from their nodes
10708
10709     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10710     int ielem;
10711     for ( ielem=1;  elemItr != theElems.end(); ++elemItr )
10712     {
10713       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10714       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10715         continue;
10716       gp_XYZ normal;
10717       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10718       MESSAGE("element " << ielem++ <<  " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10719       std::set<const SMDS_MeshNode*> nodesElem;
10720       nodesElem.clear();
10721       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10722       while ( nodeItr->more() )
10723       {
10724         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10725         nodesElem.insert(aNode);
10726       }
10727       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10728       for (; nodit != nodesElem.end(); nodit++)
10729       {
10730         MESSAGE("  noeud ");
10731         const SMDS_MeshNode* aNode = *nodit;
10732         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10733           continue;
10734         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10735           continue;
10736         alreadyCheckedNodes.insert(aNode);
10737         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10738         while ( backElemItr->more() )
10739         {
10740           MESSAGE("    backelem ");
10741           const SMDS_MeshElement* curElem = backElemItr->next();
10742           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10743             continue;
10744           if (theElems.find(curElem) != theElems.end())
10745             continue;
10746           alreadyCheckedElems.insert(curElem);
10747           double x=0, y=0, z=0;
10748           int nb = 0;
10749           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10750           while ( nodeItr2->more() )
10751           {
10752             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10753             x += anotherNode->X();
10754             y += anotherNode->Y();
10755             z += anotherNode->Z();
10756             nb++;
10757           }
10758           gp_XYZ p;
10759           p.SetCoord( x/nb -aNode->X(),
10760                       y/nb -aNode->Y(),
10761                       z/nb -aNode->Z() );
10762           MESSAGE("      check " << p.X() << " " << p.Y() << " " << p.Z());
10763           if (normal*p > 0)
10764           {
10765             MESSAGE("    --- inserted")
10766             theAffectedElems.insert( curElem );
10767           }
10768           else if (curElem->GetType() == SMDSAbs_Edge)
10769             edgesToCheck.insert(curElem);
10770         }
10771       }
10772     }
10773     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10774     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10775     for( ; eit != edgesToCheck.end(); eit++)
10776     {
10777       bool onside = true;
10778       const SMDS_MeshElement* anEdge = *eit;
10779       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10780       while ( nodeItr->more() )
10781       {
10782         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10783         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10784         {
10785           onside = false;
10786           break;
10787         }
10788       }
10789       if (onside)
10790       {
10791         MESSAGE("    --- edge onside inserted")
10792         theAffectedElems.insert(anEdge);
10793       }
10794     }
10795   }
10796   else
10797   {
10798     const double aTol = Precision::Confusion();
10799     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10800     auto_ptr<_FaceClassifier>              aFaceClassifier;
10801     if ( theShape.ShapeType() == TopAbs_SOLID )
10802     {
10803       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10804       bsc3d->PerformInfinitePoint(aTol);
10805     }
10806     else if (theShape.ShapeType() == TopAbs_FACE )
10807     {
10808       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10809     }
10810
10811     // iterates on indicated elements and get elements by back references from their nodes
10812     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10813     int ielem;
10814     for ( ielem = 1;  elemItr != theElems.end(); ++elemItr )
10815     {
10816       MESSAGE("element " << ielem++);
10817       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10818       if (!anElem)
10819         continue;
10820       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10821       while ( nodeItr->more() )
10822       {
10823         MESSAGE("  noeud ");
10824         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10825         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10826           continue;
10827         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10828         while ( backElemItr->more() )
10829         {
10830           MESSAGE("    backelem ");
10831           const SMDS_MeshElement* curElem = backElemItr->next();
10832           if ( curElem && theElems.find(curElem) == theElems.end() &&
10833               ( bsc3d.get() ?
10834                 isInside( curElem, *bsc3d, aTol ) :
10835                 isInside( curElem, *aFaceClassifier, aTol )))
10836             theAffectedElems.insert( curElem );
10837         }
10838       }
10839     }
10840   }
10841   return true;
10842 }
10843
10844 //================================================================================
10845 /*!
10846   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10847   \param theElems - group of of elements (edges or faces) to be replicated
10848   \param theNodesNot - group of nodes not to replicate
10849   \param theShape - shape to detect affected elements (element which geometric center
10850   located on or inside shape).
10851   The replicated nodes should be associated to affected elements.
10852   \return TRUE if operation has been completed successfully, FALSE otherwise
10853 */
10854 //================================================================================
10855
10856 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10857                                             const TIDSortedElemSet& theNodesNot,
10858                                             const TopoDS_Shape&     theShape )
10859 {
10860   if ( theShape.IsNull() )
10861     return false;
10862
10863   const double aTol = Precision::Confusion();
10864   auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10865   auto_ptr<_FaceClassifier>              aFaceClassifier;
10866   if ( theShape.ShapeType() == TopAbs_SOLID )
10867   {
10868     bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10869     bsc3d->PerformInfinitePoint(aTol);
10870   }
10871   else if (theShape.ShapeType() == TopAbs_FACE )
10872   {
10873     aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10874   }
10875
10876   // iterates on indicated elements and get elements by back references from their nodes
10877   TIDSortedElemSet anAffected;
10878   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10879   for ( ;  elemItr != theElems.end(); ++elemItr )
10880   {
10881     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10882     if (!anElem)
10883       continue;
10884
10885     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10886     while ( nodeItr->more() )
10887     {
10888       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10889       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10890         continue;
10891       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10892       while ( backElemItr->more() )
10893       {
10894         const SMDS_MeshElement* curElem = backElemItr->next();
10895         if ( curElem && theElems.find(curElem) == theElems.end() &&
10896              ( bsc3d.get() ?
10897                isInside( curElem, *bsc3d, aTol ) :
10898                isInside( curElem, *aFaceClassifier, aTol )))
10899           anAffected.insert( curElem );
10900       }
10901     }
10902   }
10903   return DoubleNodes( theElems, theNodesNot, anAffected );
10904 }
10905
10906 /*!
10907  *  \brief compute an oriented angle between two planes defined by four points.
10908  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10909  *  @param p0 base of the rotation axe
10910  *  @param p1 extremity of the rotation axe
10911  *  @param g1 belongs to the first plane
10912  *  @param g2 belongs to the second plane
10913  */
10914 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10915 {
10916 //  MESSAGE("    p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10917 //  MESSAGE("    p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10918 //  MESSAGE("    g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10919 //  MESSAGE("    g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10920   gp_Vec vref(p0, p1);
10921   gp_Vec v1(p0, g1);
10922   gp_Vec v2(p0, g2);
10923   gp_Vec n1 = vref.Crossed(v1);
10924   gp_Vec n2 = vref.Crossed(v2);
10925   try {
10926     return n2.AngleWithRef(n1, vref);
10927   }
10928   catch ( Standard_Failure ) {
10929   }
10930   return Max( v1.Magnitude(), v2.Magnitude() );
10931 }
10932
10933 /*!
10934  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10935  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10936  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10937  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10938  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10939  * 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.
10940  * 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.
10941  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10942  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10943  * \param theElems - list of groups of volumes, where a group of volume is a set of
10944  *        SMDS_MeshElements sorted by Id.
10945  * \param createJointElems - if TRUE, create the elements
10946  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10947  *        the boundary between \a theDomains and the rest mesh
10948  * \return TRUE if operation has been completed successfully, FALSE otherwise
10949  */
10950 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10951                                                      bool                                 createJointElems,
10952                                                      bool                                 onAllBoundaries)
10953 {
10954   MESSAGE("----------------------------------------------");
10955   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10956   MESSAGE("----------------------------------------------");
10957
10958   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10959   meshDS->BuildDownWardConnectivity(true);
10960   CHRONO(50);
10961   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10962
10963   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10964   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10965   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10966
10967   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10968   std::map<int,int>celldom; // cell vtkId --> domain
10969   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10970   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10971   faceDomains.clear();
10972   celldom.clear();
10973   cellDomains.clear();
10974   nodeDomains.clear();
10975   std::map<int,int> emptyMap;
10976   std::set<int> emptySet;
10977   emptyMap.clear();
10978
10979   MESSAGE(".. Number of domains :"<<theElems.size());
10980
10981   TIDSortedElemSet theRestDomElems;
10982   const int iRestDom  = -1;
10983   const int idom0     = onAllBoundaries ? iRestDom : 0;
10984   const int nbDomains = theElems.size();
10985
10986   // Check if the domains do not share an element
10987   for (int idom = 0; idom < nbDomains-1; idom++)
10988     {
10989 //       MESSAGE("... Check of domain #" << idom);
10990       const TIDSortedElemSet& domain = theElems[idom];
10991       TIDSortedElemSet::const_iterator elemItr = domain.begin();
10992       for (; elemItr != domain.end(); ++elemItr)
10993         {
10994           const SMDS_MeshElement* anElem = *elemItr;
10995           int idombisdeb = idom + 1 ;
10996           for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10997           {
10998             const TIDSortedElemSet& domainbis = theElems[idombis];
10999             if ( domainbis.count(anElem) )
11000             {
11001               MESSAGE(".... Domain #" << idom);
11002               MESSAGE(".... Domain #" << idombis);
11003               throw SALOME_Exception("The domains are not disjoint.");
11004               return false ;
11005             }
11006           }
11007         }
11008     }
11009
11010   for (int idom = 0; idom < nbDomains; idom++)
11011     {
11012
11013       // --- build a map (face to duplicate --> volume to modify)
11014       //     with all the faces shared by 2 domains (group of elements)
11015       //     and corresponding volume of this domain, for each shared face.
11016       //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11017
11018       MESSAGE("... Neighbors of domain #" << idom);
11019       const TIDSortedElemSet& domain = theElems[idom];
11020       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11021       for (; elemItr != domain.end(); ++elemItr)
11022         {
11023           const SMDS_MeshElement* anElem = *elemItr;
11024           if (!anElem)
11025             continue;
11026           int vtkId = anElem->getVtkId();
11027           //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
11028           int neighborsVtkIds[NBMAXNEIGHBORS];
11029           int downIds[NBMAXNEIGHBORS];
11030           unsigned char downTypes[NBMAXNEIGHBORS];
11031           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11032           for (int n = 0; n < nbNeighbors; n++)
11033             {
11034               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11035               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11036               if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11037                 {
11038                   bool ok = false ;
11039                   for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11040                   {
11041                     // MESSAGE("Domain " << idombis);
11042                     const TIDSortedElemSet& domainbis = theElems[idombis];
11043                     if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11044                   }
11045                   if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11046                   {
11047                     DownIdType face(downIds[n], downTypes[n]);
11048                     if (!faceDomains[face].count(idom))
11049                       {
11050                         faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11051                         celldom[vtkId] = idom;
11052                         //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
11053                       }
11054                     if ( !ok )
11055                     {
11056                       theRestDomElems.insert( elem );
11057                       faceDomains[face][iRestDom] = neighborsVtkIds[n];
11058                       celldom[neighborsVtkIds[n]] = iRestDom;
11059                     }
11060                   }
11061                 }
11062             }
11063         }
11064     }
11065
11066   //MESSAGE("Number of shared faces " << faceDomains.size());
11067   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11068
11069   // --- explore the shared faces domain by domain,
11070   //     explore the nodes of the face and see if they belong to a cell in the domain,
11071   //     which has only a node or an edge on the border (not a shared face)
11072
11073   for (int idomain = idom0; idomain < nbDomains; idomain++)
11074     {
11075       //MESSAGE("Domain " << idomain);
11076       const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11077       itface = faceDomains.begin();
11078       for (; itface != faceDomains.end(); ++itface)
11079         {
11080           const std::map<int, int>& domvol = itface->second;
11081           if (!domvol.count(idomain))
11082             continue;
11083           DownIdType face = itface->first;
11084           //MESSAGE(" --- face " << face.cellId);
11085           std::set<int> oldNodes;
11086           oldNodes.clear();
11087           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11088           std::set<int>::iterator itn = oldNodes.begin();
11089           for (; itn != oldNodes.end(); ++itn)
11090             {
11091               int oldId = *itn;
11092               //MESSAGE("     node " << oldId);
11093               vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11094               for (int i=0; i<l.ncells; i++)
11095                 {
11096                   int vtkId = l.cells[i];
11097                   const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11098                   if (!domain.count(anElem))
11099                     continue;
11100                   int vtkType = grid->GetCellType(vtkId);
11101                   int downId = grid->CellIdToDownId(vtkId);
11102                   if (downId < 0)
11103                     {
11104                       MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11105                       continue; // not OK at this stage of the algorithm:
11106                                 //no cells created after BuildDownWardConnectivity
11107                     }
11108                   DownIdType aCell(downId, vtkType);
11109                   cellDomains[aCell][idomain] = vtkId;
11110                   celldom[vtkId] = idomain;
11111                   //MESSAGE("       cell " << vtkId << " domain " << idomain);
11112                 }
11113             }
11114         }
11115     }
11116
11117   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11118   //     for each shared face, get the nodes
11119   //     for each node, for each domain of the face, create a clone of the node
11120
11121   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11122   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11123   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11124
11125   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11126   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11127   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11128
11129   MESSAGE(".. Duplication of the nodes");
11130   for (int idomain = idom0; idomain < nbDomains; idomain++)
11131     {
11132       itface = faceDomains.begin();
11133       for (; itface != faceDomains.end(); ++itface)
11134         {
11135           const std::map<int, int>& domvol = itface->second;
11136           if (!domvol.count(idomain))
11137             continue;
11138           DownIdType face = itface->first;
11139           //MESSAGE(" --- face " << face.cellId);
11140           std::set<int> oldNodes;
11141           oldNodes.clear();
11142           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11143           std::set<int>::iterator itn = oldNodes.begin();
11144           for (; itn != oldNodes.end(); ++itn)
11145             {
11146               int oldId = *itn;
11147               if (nodeDomains[oldId].empty())
11148                 {
11149                   nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11150                   //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11151                 }
11152               std::map<int, int>::const_iterator itdom = domvol.begin();
11153               for (; itdom != domvol.end(); ++itdom)
11154                 {
11155                   int idom = itdom->first;
11156                   //MESSAGE("         domain " << idom);
11157                   if (!nodeDomains[oldId].count(idom)) // --- node to clone
11158                     {
11159                       if (nodeDomains[oldId].size() >= 2) // a multiple node
11160                         {
11161                           vector<int> orderedDoms;
11162                           //MESSAGE("multiple node " << oldId);
11163                           if (mutipleNodes.count(oldId))
11164                             orderedDoms = mutipleNodes[oldId];
11165                           else
11166                             {
11167                               map<int,int>::iterator it = nodeDomains[oldId].begin();
11168                               for (; it != nodeDomains[oldId].end(); ++it)
11169                                 orderedDoms.push_back(it->first);
11170                             }
11171                           orderedDoms.push_back(idom); // TODO order ==> push_front or back
11172                           //stringstream txt;
11173                           //for (int i=0; i<orderedDoms.size(); i++)
11174                           //  txt << orderedDoms[i] << " ";
11175                           //MESSAGE("orderedDoms " << txt.str());
11176                           mutipleNodes[oldId] = orderedDoms;
11177                         }
11178                       double *coords = grid->GetPoint(oldId);
11179                       SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11180                       copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11181                       int newId = newNode->getVtkId();
11182                       nodeDomains[oldId][idom] = newId; // cloned node for other domains
11183                       //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11184                     }
11185                 }
11186             }
11187         }
11188     }
11189
11190   MESSAGE(".. Creation of elements");
11191   for (int idomain = idom0; idomain < nbDomains; idomain++)
11192     {
11193       itface = faceDomains.begin();
11194       for (; itface != faceDomains.end(); ++itface)
11195         {
11196           std::map<int, int> domvol = itface->second;
11197           if (!domvol.count(idomain))
11198             continue;
11199           DownIdType face = itface->first;
11200           //MESSAGE(" --- face " << face.cellId);
11201           std::set<int> oldNodes;
11202           oldNodes.clear();
11203           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11204           int nbMultipleNodes = 0;
11205           std::set<int>::iterator itn = oldNodes.begin();
11206           for (; itn != oldNodes.end(); ++itn)
11207             {
11208               int oldId = *itn;
11209               if (mutipleNodes.count(oldId))
11210                 nbMultipleNodes++;
11211             }
11212           if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11213             {
11214               //MESSAGE("multiple Nodes detected on a shared face");
11215               int downId = itface->first.cellId;
11216               unsigned char cellType = itface->first.cellType;
11217               // --- shared edge or shared face ?
11218               if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11219                 {
11220                   int nodes[3];
11221                   int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11222                   for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11223                     if (mutipleNodes.count(nodes[i]))
11224                       if (!mutipleNodesToFace.count(nodes[i]))
11225                         mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11226                 }
11227               else // shared face (between two volumes)
11228                 {
11229                   int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11230                   const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11231                   const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11232                   for (int ie =0; ie < nbEdges; ie++)
11233                     {
11234                       int nodes[3];
11235                       int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11236                       if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11237                         {
11238                           vector<int> vn0 = mutipleNodes[nodes[0]];
11239                           vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11240                           vector<int> doms;
11241                           for (int i0 = 0; i0 < vn0.size(); i0++)
11242                             for (int i1 = 0; i1 < vn1.size(); i1++)
11243                               if (vn0[i0] == vn1[i1])
11244                                 doms.push_back(vn0[i0]);
11245                           if (doms.size() >2)
11246                             {
11247                               //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11248                               double *coords = grid->GetPoint(nodes[0]);
11249                               gp_Pnt p0(coords[0], coords[1], coords[2]);
11250                               coords = grid->GetPoint(nodes[nbNodes - 1]);
11251                               gp_Pnt p1(coords[0], coords[1], coords[2]);
11252                               gp_Pnt gref;
11253                               int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11254                               map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11255                               map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11256                               int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11257                               for (int id=0; id < doms.size(); id++)
11258                                 {
11259                                   int idom = doms[id];
11260                                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11261                                   for (int ivol=0; ivol<nbvol; ivol++)
11262                                     {
11263                                       int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11264                                       SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11265                                       if (domain.count(elem))
11266                                         {
11267                                           SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11268                                           domvol[idom] = svol;
11269                                           //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11270                                           double values[3];
11271                                           vtkIdType npts = 0;
11272                                           vtkIdType* pts = 0;
11273                                           grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11274                                           SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11275                                           if (id ==0)
11276                                             {
11277                                               gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11278                                               angleDom[idom] = 0;
11279                                             }
11280                                           else
11281                                             {
11282                                               gp_Pnt g(values[0], values[1], values[2]);
11283                                               angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11284                                               //MESSAGE("  angle=" << angleDom[idom]);
11285                                             }
11286                                           break;
11287                                         }
11288                                     }
11289                                 }
11290                               map<double, int> sortedDom; // sort domains by angle
11291                               for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11292                                 sortedDom[ia->second] = ia->first;
11293                               vector<int> vnodes;
11294                               vector<int> vdom;
11295                               for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11296                                 {
11297                                   vdom.push_back(ib->second);
11298                                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11299                                 }
11300                               for (int ino = 0; ino < nbNodes; ino++)
11301                                 vnodes.push_back(nodes[ino]);
11302                               edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11303                             }
11304                         }
11305                     }
11306                 }
11307             }
11308         }
11309     }
11310
11311   // --- iterate on shared faces (volumes to modify, face to extrude)
11312   //     get node id's of the face (id SMDS = id VTK)
11313   //     create flat element with old and new nodes if requested
11314
11315   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11316   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11317
11318   std::map<int, std::map<long,int> > nodeQuadDomains;
11319   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11320
11321   MESSAGE(".. Creation of elements: simple junction");
11322   if (createJointElems)
11323     {
11324       int idg;
11325       string joints2DName = "joints2D";
11326       mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11327       SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11328       string joints3DName = "joints3D";
11329       mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11330       SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11331
11332       itface = faceDomains.begin();
11333       for (; itface != faceDomains.end(); ++itface)
11334         {
11335           DownIdType face = itface->first;
11336           std::set<int> oldNodes;
11337           std::set<int>::iterator itn;
11338           oldNodes.clear();
11339           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11340
11341           std::map<int, int> domvol = itface->second;
11342           std::map<int, int>::iterator itdom = domvol.begin();
11343           int dom1 = itdom->first;
11344           int vtkVolId = itdom->second;
11345           itdom++;
11346           int dom2 = itdom->first;
11347           SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11348                                                              nodeQuadDomains);
11349           stringstream grpname;
11350           grpname << "j_";
11351           if (dom1 < dom2)
11352             grpname << dom1 << "_" << dom2;
11353           else
11354             grpname << dom2 << "_" << dom1;
11355           string namegrp = grpname.str();
11356           if (!mapOfJunctionGroups.count(namegrp))
11357             mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11358           SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11359           if (sgrp)
11360             sgrp->Add(vol->GetID());
11361           if (vol->GetType() == SMDSAbs_Volume)
11362             joints3DGrp->Add(vol->GetID());
11363           else if (vol->GetType() == SMDSAbs_Face)
11364             joints2DGrp->Add(vol->GetID());
11365         }
11366     }
11367
11368   // --- create volumes on multiple domain intersection if requested
11369   //     iterate on mutipleNodesToFace
11370   //     iterate on edgesMultiDomains
11371
11372   MESSAGE(".. Creation of elements: multiple junction");
11373   if (createJointElems)
11374     {
11375       // --- iterate on mutipleNodesToFace
11376
11377       std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11378       for (; itn != mutipleNodesToFace.end(); ++itn)
11379         {
11380           int node = itn->first;
11381           vector<int> orderDom = itn->second;
11382           vector<vtkIdType> orderedNodes;
11383           for (int idom = 0; idom <orderDom.size(); idom++)
11384             orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11385             SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11386
11387             stringstream grpname;
11388             grpname << "m2j_";
11389             grpname << 0 << "_" << 0;
11390             int idg;
11391             string namegrp = grpname.str();
11392             if (!mapOfJunctionGroups.count(namegrp))
11393               mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11394             SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11395             if (sgrp)
11396               sgrp->Add(face->GetID());
11397         }
11398
11399       // --- iterate on edgesMultiDomains
11400
11401       std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11402       for (; ite != edgesMultiDomains.end(); ++ite)
11403         {
11404           vector<int> nodes = ite->first;
11405           vector<int> orderDom = ite->second;
11406           vector<vtkIdType> orderedNodes;
11407           if (nodes.size() == 2)
11408             {
11409               //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11410               for (int ino=0; ino < nodes.size(); ino++)
11411                 if (orderDom.size() == 3)
11412                   for (int idom = 0; idom <orderDom.size(); idom++)
11413                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11414                 else
11415                   for (int idom = orderDom.size()-1; idom >=0; idom--)
11416                     orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11417               SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11418
11419               int idg;
11420               string namegrp = "jointsMultiples";
11421               if (!mapOfJunctionGroups.count(namegrp))
11422                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11423               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11424               if (sgrp)
11425                 sgrp->Add(vol->GetID());
11426             }
11427           else
11428             {
11429               INFOS("Quadratic multiple joints not implemented");
11430               // TODO quadratic nodes
11431             }
11432         }
11433     }
11434
11435   // --- list the explicit faces and edges of the mesh that need to be modified,
11436   //     i.e. faces and edges built with one or more duplicated nodes.
11437   //     associate these faces or edges to their corresponding domain.
11438   //     only the first domain found is kept when a face or edge is shared
11439
11440   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11441   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11442   faceOrEdgeDom.clear();
11443   feDom.clear();
11444
11445   MESSAGE(".. Modification of elements");
11446   for (int idomain = idom0; idomain < nbDomains; idomain++)
11447     {
11448       std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11449       for (; itnod != nodeDomains.end(); ++itnod)
11450         {
11451           int oldId = itnod->first;
11452           //MESSAGE("     node " << oldId);
11453           vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11454           for (int i = 0; i < l.ncells; i++)
11455             {
11456               int vtkId = l.cells[i];
11457               int vtkType = grid->GetCellType(vtkId);
11458               int downId = grid->CellIdToDownId(vtkId);
11459               if (downId < 0)
11460                 continue; // new cells: not to be modified
11461               DownIdType aCell(downId, vtkType);
11462               int volParents[1000];
11463               int nbvol = grid->GetParentVolumes(volParents, vtkId);
11464               for (int j = 0; j < nbvol; j++)
11465                 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11466                   if (!feDom.count(vtkId))
11467                     {
11468                       feDom[vtkId] = idomain;
11469                       faceOrEdgeDom[aCell] = emptyMap;
11470                       faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11471                       //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11472                       //        << " type " << vtkType << " downId " << downId);
11473                     }
11474             }
11475         }
11476     }
11477
11478   // --- iterate on shared faces (volumes to modify, face to extrude)
11479   //     get node id's of the face
11480   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11481
11482   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11483   for (int m=0; m<3; m++)
11484     {
11485       std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11486       itface = (*amap).begin();
11487       for (; itface != (*amap).end(); ++itface)
11488         {
11489           DownIdType face = itface->first;
11490           std::set<int> oldNodes;
11491           std::set<int>::iterator itn;
11492           oldNodes.clear();
11493           grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11494           //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11495           std::map<int, int> localClonedNodeIds;
11496
11497           std::map<int, int> domvol = itface->second;
11498           std::map<int, int>::iterator itdom = domvol.begin();
11499           for (; itdom != domvol.end(); ++itdom)
11500             {
11501               int idom = itdom->first;
11502               int vtkVolId = itdom->second;
11503               //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11504               localClonedNodeIds.clear();
11505               for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11506                 {
11507                   int oldId = *itn;
11508                   if (nodeDomains[oldId].count(idom))
11509                     {
11510                       localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11511                       //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11512                     }
11513                 }
11514               meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11515             }
11516         }
11517     }
11518
11519   // Remove empty groups (issue 0022812)
11520   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11521   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11522   {
11523     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11524       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11525   }
11526
11527   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11528   grid->BuildLinks();
11529
11530   CHRONOSTOP(50);
11531   counters::stats();
11532   return true;
11533 }
11534
11535 /*!
11536  * \brief Double nodes on some external faces and create flat elements.
11537  * Flat elements are mainly used by some types of mechanic calculations.
11538  *
11539  * Each group of the list must be constituted of faces.
11540  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11541  * @param theElems - list of groups of faces, where a group of faces is a set of
11542  * SMDS_MeshElements sorted by Id.
11543  * @return TRUE if operation has been completed successfully, FALSE otherwise
11544  */
11545 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11546 {
11547   MESSAGE("-------------------------------------------------");
11548   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11549   MESSAGE("-------------------------------------------------");
11550
11551   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11552
11553   // --- For each group of faces
11554   //     duplicate the nodes, create a flat element based on the face
11555   //     replace the nodes of the faces by their clones
11556
11557   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11558   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11559   clonedNodes.clear();
11560   intermediateNodes.clear();
11561   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11562   mapOfJunctionGroups.clear();
11563
11564   for (int idom = 0; idom < theElems.size(); idom++)
11565     {
11566       const TIDSortedElemSet& domain = theElems[idom];
11567       TIDSortedElemSet::const_iterator elemItr = domain.begin();
11568       for (; elemItr != domain.end(); ++elemItr)
11569         {
11570           SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11571           SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11572           if (!aFace)
11573             continue;
11574           // MESSAGE("aFace=" << aFace->GetID());
11575           bool isQuad = aFace->IsQuadratic();
11576           vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11577
11578           // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11579
11580           SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11581           while (nodeIt->more())
11582             {
11583               const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11584               bool isMedium = isQuad && (aFace->IsMediumNode(node));
11585               if (isMedium)
11586                 ln2.push_back(node);
11587               else
11588                 ln0.push_back(node);
11589
11590               const SMDS_MeshNode* clone = 0;
11591               if (!clonedNodes.count(node))
11592                 {
11593                   clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11594                   copyPosition( node, clone );
11595                   clonedNodes[node] = clone;
11596                 }
11597               else
11598                 clone = clonedNodes[node];
11599
11600               if (isMedium)
11601                 ln3.push_back(clone);
11602               else
11603                 ln1.push_back(clone);
11604
11605               const SMDS_MeshNode* inter = 0;
11606               if (isQuad && (!isMedium))
11607                 {
11608                   if (!intermediateNodes.count(node))
11609                     {
11610                       inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11611                       copyPosition( node, inter );
11612                       intermediateNodes[node] = inter;
11613                     }
11614                   else
11615                     inter = intermediateNodes[node];
11616                   ln4.push_back(inter);
11617                 }
11618             }
11619
11620           // --- extrude the face
11621
11622           vector<const SMDS_MeshNode*> ln;
11623           SMDS_MeshVolume* vol = 0;
11624           vtkIdType aType = aFace->GetVtkType();
11625           switch (aType)
11626           {
11627             case VTK_TRIANGLE:
11628               vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11629               // MESSAGE("vol prism " << vol->GetID());
11630               ln.push_back(ln1[0]);
11631               ln.push_back(ln1[1]);
11632               ln.push_back(ln1[2]);
11633               break;
11634             case VTK_QUAD:
11635               vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11636               // MESSAGE("vol hexa " << vol->GetID());
11637               ln.push_back(ln1[0]);
11638               ln.push_back(ln1[1]);
11639               ln.push_back(ln1[2]);
11640               ln.push_back(ln1[3]);
11641               break;
11642             case VTK_QUADRATIC_TRIANGLE:
11643               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11644                                       ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11645               // MESSAGE("vol quad prism " << vol->GetID());
11646               ln.push_back(ln1[0]);
11647               ln.push_back(ln1[1]);
11648               ln.push_back(ln1[2]);
11649               ln.push_back(ln3[0]);
11650               ln.push_back(ln3[1]);
11651               ln.push_back(ln3[2]);
11652               break;
11653             case VTK_QUADRATIC_QUAD:
11654 //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11655 //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11656 //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11657               vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11658                                       ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11659                                       ln4[0], ln4[1], ln4[2], ln4[3]);
11660               // MESSAGE("vol quad hexa " << vol->GetID());
11661               ln.push_back(ln1[0]);
11662               ln.push_back(ln1[1]);
11663               ln.push_back(ln1[2]);
11664               ln.push_back(ln1[3]);
11665               ln.push_back(ln3[0]);
11666               ln.push_back(ln3[1]);
11667               ln.push_back(ln3[2]);
11668               ln.push_back(ln3[3]);
11669               break;
11670             case VTK_POLYGON:
11671               break;
11672             default:
11673               break;
11674           }
11675
11676           if (vol)
11677             {
11678               stringstream grpname;
11679               grpname << "jf_";
11680               grpname << idom;
11681               int idg;
11682               string namegrp = grpname.str();
11683               if (!mapOfJunctionGroups.count(namegrp))
11684                 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11685               SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11686               if (sgrp)
11687                 sgrp->Add(vol->GetID());
11688             }
11689
11690           // --- modify the face
11691
11692           aFace->ChangeNodes(&ln[0], ln.size());
11693         }
11694     }
11695   return true;
11696 }
11697
11698 /*!
11699  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11700  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11701  *  groups of faces to remove inside the object, (idem edges).
11702  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11703  */
11704 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11705                                       const TopoDS_Shape& theShape,
11706                                       SMESH_NodeSearcher* theNodeSearcher,
11707                                       const char* groupName,
11708                                       std::vector<double>&   nodesCoords,
11709                                       std::vector<std::vector<int> >& listOfListOfNodes)
11710 {
11711   MESSAGE("--------------------------------");
11712   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11713   MESSAGE("--------------------------------");
11714
11715   // --- zone of volumes to remove is given :
11716   //     1 either by a geom shape (one or more vertices) and a radius,
11717   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11718   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11719   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11720   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11721   //     defined by it's name.
11722
11723   SMESHDS_GroupBase* groupDS = 0;
11724   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11725   while ( groupIt->more() )
11726     {
11727       groupDS = 0;
11728       SMESH_Group * group = groupIt->next();
11729       if ( !group ) continue;
11730       groupDS = group->GetGroupDS();
11731       if ( !groupDS || groupDS->IsEmpty() ) continue;
11732       std::string grpName = group->GetName();
11733       //MESSAGE("grpName=" << grpName);
11734       if (grpName == groupName)
11735         break;
11736       else
11737         groupDS = 0;
11738     }
11739
11740   bool isNodeGroup = false;
11741   bool isNodeCoords = false;
11742   if (groupDS)
11743     {
11744       if (groupDS->GetType() != SMDSAbs_Node)
11745         return;
11746       isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11747     }
11748
11749   if (nodesCoords.size() > 0)
11750     isNodeCoords = true; // a list o nodes given by their coordinates
11751   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11752
11753   // --- define groups to build
11754
11755   int idg; // --- group of SMDS volumes
11756   string grpvName = groupName;
11757   grpvName += "_vol";
11758   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11759   if (!grp)
11760     {
11761       MESSAGE("group not created " << grpvName);
11762       return;
11763     }
11764   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11765
11766   int idgs; // --- group of SMDS faces on the skin
11767   string grpsName = groupName;
11768   grpsName += "_skin";
11769   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11770   if (!grps)
11771     {
11772       MESSAGE("group not created " << grpsName);
11773       return;
11774     }
11775   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11776
11777   int idgi; // --- group of SMDS faces internal (several shapes)
11778   string grpiName = groupName;
11779   grpiName += "_internalFaces";
11780   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11781   if (!grpi)
11782     {
11783       MESSAGE("group not created " << grpiName);
11784       return;
11785     }
11786   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11787
11788   int idgei; // --- group of SMDS faces internal (several shapes)
11789   string grpeiName = groupName;
11790   grpeiName += "_internalEdges";
11791   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11792   if (!grpei)
11793     {
11794       MESSAGE("group not created " << grpeiName);
11795       return;
11796     }
11797   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11798
11799   // --- build downward connectivity
11800
11801   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11802   meshDS->BuildDownWardConnectivity(true);
11803   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11804
11805   // --- set of volumes detected inside
11806
11807   std::set<int> setOfInsideVol;
11808   std::set<int> setOfVolToCheck;
11809
11810   std::vector<gp_Pnt> gpnts;
11811   gpnts.clear();
11812
11813   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11814     {
11815       MESSAGE("group of nodes provided");
11816       SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11817       while ( elemIt->more() )
11818         {
11819           const SMDS_MeshElement* elem = elemIt->next();
11820           if (!elem)
11821             continue;
11822           const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11823           if (!node)
11824             continue;
11825           SMDS_MeshElement* vol = 0;
11826           SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11827           while (volItr->more())
11828             {
11829               vol = (SMDS_MeshElement*)volItr->next();
11830               setOfInsideVol.insert(vol->getVtkId());
11831               sgrp->Add(vol->GetID());
11832             }
11833         }
11834     }
11835   else if (isNodeCoords)
11836     {
11837       MESSAGE("list of nodes coordinates provided");
11838       int i = 0;
11839       int k = 0;
11840       while (i < nodesCoords.size()-2)
11841         {
11842           double x = nodesCoords[i++];
11843           double y = nodesCoords[i++];
11844           double z = nodesCoords[i++];
11845           gp_Pnt p = gp_Pnt(x, y ,z);
11846           gpnts.push_back(p);
11847           MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11848           k++;
11849         }
11850     }
11851   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11852     {
11853       MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11854       TopTools_IndexedMapOfShape vertexMap;
11855       TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11856       gp_Pnt p = gp_Pnt(0,0,0);
11857       if (vertexMap.Extent() < 1)
11858         return;
11859
11860       for ( int i = 1; i <= vertexMap.Extent(); ++i )
11861         {
11862           const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11863           p = BRep_Tool::Pnt(vertex);
11864           gpnts.push_back(p);
11865           MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11866         }
11867     }
11868
11869   if (gpnts.size() > 0)
11870     {
11871       int nodeId = 0;
11872       const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11873       if (startNode)
11874         nodeId = startNode->GetID();
11875       MESSAGE("nodeId " << nodeId);
11876
11877       double radius2 = radius*radius;
11878       MESSAGE("radius2 " << radius2);
11879
11880       // --- volumes on start node
11881
11882       setOfVolToCheck.clear();
11883       SMDS_MeshElement* startVol = 0;
11884       SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11885       while (volItr->more())
11886         {
11887           startVol = (SMDS_MeshElement*)volItr->next();
11888           setOfVolToCheck.insert(startVol->getVtkId());
11889         }
11890       if (setOfVolToCheck.empty())
11891         {
11892           MESSAGE("No volumes found");
11893           return;
11894         }
11895
11896       // --- starting with central volumes then their neighbors, check if they are inside
11897       //     or outside the domain, until no more new neighbor volume is inside.
11898       //     Fill the group of inside volumes
11899
11900       std::map<int, double> mapOfNodeDistance2;
11901       mapOfNodeDistance2.clear();
11902       std::set<int> setOfOutsideVol;
11903       while (!setOfVolToCheck.empty())
11904         {
11905           std::set<int>::iterator it = setOfVolToCheck.begin();
11906           int vtkId = *it;
11907           MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11908           bool volInside = false;
11909           vtkIdType npts = 0;
11910           vtkIdType* pts = 0;
11911           grid->GetCellPoints(vtkId, npts, pts);
11912           for (int i=0; i<npts; i++)
11913             {
11914               double distance2 = 0;
11915               if (mapOfNodeDistance2.count(pts[i]))
11916                 {
11917                   distance2 = mapOfNodeDistance2[pts[i]];
11918                   MESSAGE("point " << pts[i] << " distance2 " << distance2);
11919                 }
11920               else
11921                 {
11922                   double *coords = grid->GetPoint(pts[i]);
11923                   gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11924                   distance2 = 1.E40;
11925                   for (int j=0; j<gpnts.size(); j++)
11926                     {
11927                       double d2 = aPoint.SquareDistance(gpnts[j]);
11928                       if (d2 < distance2)
11929                         {
11930                           distance2 = d2;
11931                           if (distance2 < radius2)
11932                             break;
11933                         }
11934                     }
11935                   mapOfNodeDistance2[pts[i]] = distance2;
11936                   MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11937                 }
11938               if (distance2 < radius2)
11939                 {
11940                   volInside = true; // one or more nodes inside the domain
11941                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11942                   break;
11943                 }
11944             }
11945           if (volInside)
11946             {
11947               setOfInsideVol.insert(vtkId);
11948               MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11949               int neighborsVtkIds[NBMAXNEIGHBORS];
11950               int downIds[NBMAXNEIGHBORS];
11951               unsigned char downTypes[NBMAXNEIGHBORS];
11952               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11953               for (int n = 0; n < nbNeighbors; n++)
11954                 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11955                   setOfVolToCheck.insert(neighborsVtkIds[n]);
11956             }
11957           else
11958             {
11959               setOfOutsideVol.insert(vtkId);
11960               MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11961             }
11962           setOfVolToCheck.erase(vtkId);
11963         }
11964     }
11965
11966   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11967   //     If yes, add the volume to the inside set
11968
11969   bool addedInside = true;
11970   std::set<int> setOfVolToReCheck;
11971   while (addedInside)
11972     {
11973       MESSAGE(" --------------------------- re check");
11974       addedInside = false;
11975       std::set<int>::iterator itv = setOfInsideVol.begin();
11976       for (; itv != setOfInsideVol.end(); ++itv)
11977         {
11978           int vtkId = *itv;
11979           int neighborsVtkIds[NBMAXNEIGHBORS];
11980           int downIds[NBMAXNEIGHBORS];
11981           unsigned char downTypes[NBMAXNEIGHBORS];
11982           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11983           for (int n = 0; n < nbNeighbors; n++)
11984             if (!setOfInsideVol.count(neighborsVtkIds[n]))
11985               setOfVolToReCheck.insert(neighborsVtkIds[n]);
11986         }
11987       setOfVolToCheck = setOfVolToReCheck;
11988       setOfVolToReCheck.clear();
11989       while  (!setOfVolToCheck.empty())
11990         {
11991           std::set<int>::iterator it = setOfVolToCheck.begin();
11992           int vtkId = *it;
11993           if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11994             {
11995               MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11996               int countInside = 0;
11997               int neighborsVtkIds[NBMAXNEIGHBORS];
11998               int downIds[NBMAXNEIGHBORS];
11999               unsigned char downTypes[NBMAXNEIGHBORS];
12000               int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12001               for (int n = 0; n < nbNeighbors; n++)
12002                 if (setOfInsideVol.count(neighborsVtkIds[n]))
12003                   countInside++;
12004               MESSAGE("countInside " << countInside);
12005               if (countInside > 1)
12006                 {
12007                   MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12008                   setOfInsideVol.insert(vtkId);
12009                   sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12010                   addedInside = true;
12011                 }
12012               else
12013                 setOfVolToReCheck.insert(vtkId);
12014             }
12015           setOfVolToCheck.erase(vtkId);
12016         }
12017     }
12018
12019   // --- map of Downward faces at the boundary, inside the global volume
12020   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12021   //     fill group of SMDS faces inside the volume (when several volume shapes)
12022   //     fill group of SMDS faces on the skin of the global volume (if skin)
12023
12024   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12025   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
12026   std::set<int>::iterator it = setOfInsideVol.begin();
12027   for (; it != setOfInsideVol.end(); ++it)
12028     {
12029       int vtkId = *it;
12030       //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12031       int neighborsVtkIds[NBMAXNEIGHBORS];
12032       int downIds[NBMAXNEIGHBORS];
12033       unsigned char downTypes[NBMAXNEIGHBORS];
12034       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12035       for (int n = 0; n < nbNeighbors; n++)
12036         {
12037           int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12038           if (neighborDim == 3)
12039             {
12040               if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12041                 {
12042                   DownIdType face(downIds[n], downTypes[n]);
12043                   boundaryFaces[face] = vtkId;
12044                 }
12045               // if the face between to volumes is in the mesh, get it (internal face between shapes)
12046               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12047               if (vtkFaceId >= 0)
12048                 {
12049                   sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12050                   // find also the smds edges on this face
12051                   int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12052                   const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12053                   const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12054                   for (int i = 0; i < nbEdges; i++)
12055                     {
12056                       int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12057                       if (vtkEdgeId >= 0)
12058                         sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12059                     }
12060                 }
12061             }
12062           else if (neighborDim == 2) // skin of the volume
12063             {
12064               DownIdType face(downIds[n], downTypes[n]);
12065               skinFaces[face] = vtkId;
12066               int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12067               if (vtkFaceId >= 0)
12068                 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12069             }
12070         }
12071     }
12072
12073   // --- identify the edges constituting the wire of each subshape on the skin
12074   //     define polylines with the nodes of edges, equivalent to wires
12075   //     project polylines on subshapes, and partition, to get geom faces
12076
12077   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12078   std::set<int> emptySet;
12079   emptySet.clear();
12080   std::set<int> shapeIds;
12081
12082   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12083   while (itelem->more())
12084     {
12085       const SMDS_MeshElement *elem = itelem->next();
12086       int shapeId = elem->getshapeId();
12087       int vtkId = elem->getVtkId();
12088       if (!shapeIdToVtkIdSet.count(shapeId))
12089         {
12090           shapeIdToVtkIdSet[shapeId] = emptySet;
12091           shapeIds.insert(shapeId);
12092         }
12093       shapeIdToVtkIdSet[shapeId].insert(vtkId);
12094     }
12095
12096   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12097   std::set<DownIdType, DownIdCompare> emptyEdges;
12098   emptyEdges.clear();
12099
12100   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
12101   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12102     {
12103       int shapeId = itShape->first;
12104       MESSAGE(" --- Shape ID --- "<< shapeId);
12105       shapeIdToEdges[shapeId] = emptyEdges;
12106
12107       std::vector<int> nodesEdges;
12108
12109       std::set<int>::iterator its = itShape->second.begin();
12110       for (; its != itShape->second.end(); ++its)
12111         {
12112           int vtkId = *its;
12113           MESSAGE("     " << vtkId);
12114           int neighborsVtkIds[NBMAXNEIGHBORS];
12115           int downIds[NBMAXNEIGHBORS];
12116           unsigned char downTypes[NBMAXNEIGHBORS];
12117           int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12118           for (int n = 0; n < nbNeighbors; n++)
12119             {
12120               if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12121                 continue;
12122               int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12123               const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12124               if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12125                 {
12126                   DownIdType edge(downIds[n], downTypes[n]);
12127                   if (!shapeIdToEdges[shapeId].count(edge))
12128                     {
12129                       shapeIdToEdges[shapeId].insert(edge);
12130                       int vtkNodeId[3];
12131                       int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12132                       nodesEdges.push_back(vtkNodeId[0]);
12133                       nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12134                       MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12135                     }
12136                 }
12137             }
12138         }
12139
12140       std::list<int> order;
12141       order.clear();
12142       if (nodesEdges.size() > 0)
12143         {
12144           order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12145           nodesEdges[0] = -1;
12146           order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12147           nodesEdges[1] = -1; // do not reuse this edge
12148           bool found = true;
12149           while (found)
12150             {
12151               int nodeTofind = order.back(); // try first to push back
12152               int i = 0;
12153               for (i = 0; i<nodesEdges.size(); i++)
12154                 if (nodesEdges[i] == nodeTofind)
12155                   break;
12156               if (i == nodesEdges.size())
12157                 found = false; // no follower found on back
12158               else
12159                 {
12160                   if (i%2) // odd ==> use the previous one
12161                     if (nodesEdges[i-1] < 0)
12162                       found = false;
12163                     else
12164                       {
12165                         order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12166                         nodesEdges[i-1] = -1;
12167                       }
12168                   else // even ==> use the next one
12169                     if (nodesEdges[i+1] < 0)
12170                       found = false;
12171                     else
12172                       {
12173                         order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12174                         nodesEdges[i+1] = -1;
12175                       }
12176                 }
12177               if (found)
12178                 continue;
12179               // try to push front
12180               found = true;
12181               nodeTofind = order.front(); // try to push front
12182               for (i = 0; i<nodesEdges.size(); i++)
12183                 if (nodesEdges[i] == nodeTofind)
12184                   break;
12185               if (i == nodesEdges.size())
12186                 {
12187                   found = false; // no predecessor found on front
12188                   continue;
12189                 }
12190               if (i%2) // odd ==> use the previous one
12191                 if (nodesEdges[i-1] < 0)
12192                   found = false;
12193                 else
12194                   {
12195                     order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12196                     nodesEdges[i-1] = -1;
12197                   }
12198               else // even ==> use the next one
12199                 if (nodesEdges[i+1] < 0)
12200                   found = false;
12201                 else
12202                   {
12203                     order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12204                     nodesEdges[i+1] = -1;
12205                   }
12206             }
12207         }
12208
12209
12210       std::vector<int> nodes;
12211       nodes.push_back(shapeId);
12212       std::list<int>::iterator itl = order.begin();
12213       for (; itl != order.end(); itl++)
12214         {
12215           nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12216           MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12217         }
12218       listOfListOfNodes.push_back(nodes);
12219     }
12220
12221   //     partition geom faces with blocFissure
12222   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12223   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12224
12225   return;
12226 }
12227
12228
12229 //================================================================================
12230 /*!
12231  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12232  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12233  * \return TRUE if operation has been completed successfully, FALSE otherwise
12234  */
12235 //================================================================================
12236
12237 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12238 {
12239   // iterates on volume elements and detect all free faces on them
12240   SMESHDS_Mesh* aMesh = GetMeshDS();
12241   if (!aMesh)
12242     return false;
12243   //bool res = false;
12244   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12245   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12246   while(vIt->more())
12247   {
12248     const SMDS_MeshVolume* volume = vIt->next();
12249     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12250     vTool.SetExternalNormal();
12251     //const bool isPoly = volume->IsPoly();
12252     const int iQuad = volume->IsQuadratic();
12253     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12254     {
12255       if (!vTool.IsFreeFace(iface))
12256         continue;
12257       nbFree++;
12258       vector<const SMDS_MeshNode *> nodes;
12259       int nbFaceNodes = vTool.NbFaceNodes(iface);
12260       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12261       int inode = 0;
12262       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12263         nodes.push_back(faceNodes[inode]);
12264       if (iQuad) { // add medium nodes
12265         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12266           nodes.push_back(faceNodes[inode]);
12267         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12268           nodes.push_back(faceNodes[8]);
12269       }
12270       // add new face based on volume nodes
12271       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12272         nbExisted++;
12273         continue; // face already exsist
12274       }
12275       AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12276       nbCreated++;
12277     }
12278   }
12279   return ( nbFree==(nbExisted+nbCreated) );
12280 }
12281
12282 namespace
12283 {
12284   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12285   {
12286     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12287       return n;
12288     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12289   }
12290 }
12291 //================================================================================
12292 /*!
12293  * \brief Creates missing boundary elements
12294  *  \param elements - elements whose boundary is to be checked
12295  *  \param dimension - defines type of boundary elements to create
12296  *  \param group - a group to store created boundary elements in
12297  *  \param targetMesh - a mesh to store created boundary elements in
12298  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12299  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12300  *                                boundary elements will be copied into the targetMesh
12301  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12302  *                                boundary elements will be added into the new group
12303  *  \param aroundElements - if true, elements will be created on boundary of given
12304  *                          elements else, on boundary of the whole mesh.
12305  * \return nb of added boundary elements
12306  */
12307 //================================================================================
12308
12309 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12310                                        Bnd_Dimension           dimension,
12311                                        SMESH_Group*            group/*=0*/,
12312                                        SMESH_Mesh*             targetMesh/*=0*/,
12313                                        bool                    toCopyElements/*=false*/,
12314                                        bool                    toCopyExistingBoundary/*=false*/,
12315                                        bool                    toAddExistingBondary/*= false*/,
12316                                        bool                    aroundElements/*= false*/)
12317 {
12318   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12319   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12320   // hope that all elements are of the same type, do not check them all
12321   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12322     throw SALOME_Exception(LOCALIZED("wrong element type"));
12323
12324   if ( !targetMesh )
12325     toCopyElements = toCopyExistingBoundary = false;
12326
12327   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12328   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12329   int nbAddedBnd = 0;
12330
12331   // editor adding present bnd elements and optionally holding elements to add to the group
12332   SMESH_MeshEditor* presentEditor;
12333   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12334   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12335
12336   SMESH_MesherHelper helper( *myMesh );
12337   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12338   SMDS_VolumeTool vTool;
12339   TIDSortedElemSet avoidSet;
12340   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12341   int inode;
12342
12343   typedef vector<const SMDS_MeshNode*> TConnectivity;
12344
12345   SMDS_ElemIteratorPtr eIt;
12346   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12347   else                  eIt = elemSetIterator( elements );
12348
12349   while (eIt->more())
12350   {
12351     const SMDS_MeshElement* elem = eIt->next();
12352     const int              iQuad = elem->IsQuadratic();
12353
12354     // ------------------------------------------------------------------------------------
12355     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12356     // ------------------------------------------------------------------------------------
12357     vector<const SMDS_MeshElement*> presentBndElems;
12358     vector<TConnectivity>           missingBndElems;
12359     TConnectivity nodes, elemNodes;
12360     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12361     {
12362       vTool.SetExternalNormal();
12363       const SMDS_MeshElement* otherVol = 0;
12364       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12365       {
12366         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12367              ( !aroundElements || elements.count( otherVol )))
12368           continue;
12369         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12370         const int    nbFaceNodes = vTool.NbFaceNodes (iface);
12371         if ( missType == SMDSAbs_Edge ) // boundary edges
12372         {
12373           nodes.resize( 2+iQuad );
12374           for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12375           {
12376             for ( int j = 0; j < nodes.size(); ++j )
12377               nodes[j] =nn[i+j];
12378             if ( const SMDS_MeshElement* edge =
12379                  aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12380               presentBndElems.push_back( edge );
12381             else
12382               missingBndElems.push_back( nodes );
12383           }
12384         }
12385         else // boundary face
12386         {
12387           nodes.clear();
12388           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12389             nodes.push_back( nn[inode] ); // add corner nodes
12390           if (iQuad)
12391             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12392               nodes.push_back( nn[inode] ); // add medium nodes
12393           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12394           if ( iCenter > 0 )
12395             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12396
12397           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12398                                                                SMDSAbs_Face, /*noMedium=*/false ))
12399             presentBndElems.push_back( f );
12400           else
12401             missingBndElems.push_back( nodes );
12402
12403           if ( targetMesh != myMesh )
12404           {
12405             // add 1D elements on face boundary to be added to a new mesh
12406             const SMDS_MeshElement* edge;
12407             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12408             {
12409               if ( iQuad )
12410                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12411               else
12412                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12413               if ( edge && avoidSet.insert( edge ).second )
12414                 presentBndElems.push_back( edge );
12415             }
12416           }
12417         }
12418       }
12419     }
12420     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12421     {
12422       avoidSet.clear(), avoidSet.insert( elem );
12423       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12424                         SMDS_MeshElement::iterator() );
12425       elemNodes.push_back( elemNodes[0] );
12426       nodes.resize( 2 + iQuad );
12427       const int nbLinks = elem->NbCornerNodes();
12428       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12429       {
12430         nodes[0] = elemNodes[iN];
12431         nodes[1] = elemNodes[iN+1+iQuad];
12432         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12433           continue; // not free link
12434
12435         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12436         if ( const SMDS_MeshElement* edge =
12437              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12438           presentBndElems.push_back( edge );
12439         else
12440           missingBndElems.push_back( nodes );
12441       }
12442     }
12443
12444     // ---------------------------------
12445     // 2. Add missing boundary elements
12446     // ---------------------------------
12447     if ( targetMesh != myMesh )
12448       // instead of making a map of nodes in this mesh and targetMesh,
12449       // we create nodes with same IDs.
12450       for ( int i = 0; i < missingBndElems.size(); ++i )
12451       {
12452         TConnectivity& srcNodes = missingBndElems[i];
12453         TConnectivity  nodes( srcNodes.size() );
12454         for ( inode = 0; inode < nodes.size(); ++inode )
12455           nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12456         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12457                                                                    missType,
12458                                                                    /*noMedium=*/false))
12459           continue;
12460         tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12461         ++nbAddedBnd;
12462       }
12463     else
12464       for ( int i = 0; i < missingBndElems.size(); ++i )
12465       {
12466         TConnectivity& nodes = missingBndElems[i];
12467         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12468                                                                    missType,
12469                                                                    /*noMedium=*/false))
12470           continue;
12471         SMDS_MeshElement* elem =
12472           tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12473         ++nbAddedBnd;
12474
12475         // try to set a new element to a shape
12476         if ( myMesh->HasShapeToMesh() )
12477         {
12478           bool ok = true;
12479           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12480           const int nbN = nodes.size() / (iQuad+1 );
12481           for ( inode = 0; inode < nbN && ok; ++inode )
12482           {
12483             pair<int, TopAbs_ShapeEnum> i_stype =
12484               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12485             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12486               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12487           }
12488           if ( ok && mediumShapes.size() > 1 )
12489           {
12490             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12491             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12492             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12493             {
12494               if (( ok = ( stype_i->first != stype_i_0.first )))
12495                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12496                                         aMesh->IndexToShape( stype_i_0.second ));
12497             }
12498           }
12499           if ( ok && mediumShapes.begin()->first == missShapeType )
12500             aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12501         }
12502       }
12503
12504     // ----------------------------------
12505     // 3. Copy present boundary elements
12506     // ----------------------------------
12507     if ( toCopyExistingBoundary )
12508       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12509       {
12510         const SMDS_MeshElement* e = presentBndElems[i];
12511         TConnectivity nodes( e->NbNodes() );
12512         for ( inode = 0; inode < nodes.size(); ++inode )
12513           nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12514         presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12515       }
12516     else // store present elements to add them to a group
12517       for ( int i = 0 ; i < presentBndElems.size(); ++i )
12518       {
12519         presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12520       }
12521
12522   } // loop on given elements
12523
12524   // ---------------------------------------------
12525   // 4. Fill group with boundary elements
12526   // ---------------------------------------------
12527   if ( group )
12528   {
12529     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12530       for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12531         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12532   }
12533   tgtEditor.myLastCreatedElems.Clear();
12534   tgtEditor2.myLastCreatedElems.Clear();
12535
12536   // -----------------------
12537   // 5. Copy given elements
12538   // -----------------------
12539   if ( toCopyElements && targetMesh != myMesh )
12540   {
12541     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12542     else                  eIt = elemSetIterator( elements );
12543     while (eIt->more())
12544     {
12545       const SMDS_MeshElement* elem = eIt->next();
12546       TConnectivity nodes( elem->NbNodes() );
12547       for ( inode = 0; inode < nodes.size(); ++inode )
12548         nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12549       tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12550
12551       tgtEditor.myLastCreatedElems.Clear();
12552     }
12553   }
12554   return nbAddedBnd;
12555 }
12556
12557 //================================================================================
12558 /*!
12559  * \brief Copy node position and set \a to node on the same geometry
12560  */
12561 //================================================================================
12562
12563 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12564                                      const SMDS_MeshNode* to )
12565 {
12566   if ( !from || !to ) return;
12567
12568   SMDS_PositionPtr pos = from->GetPosition();
12569   if ( !pos || from->getshapeId() < 1 ) return;
12570
12571   switch ( pos->GetTypeOfPosition() )
12572   {
12573   case SMDS_TOP_3DSPACE: break;
12574
12575   case SMDS_TOP_FACE:
12576   {
12577     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12578     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12579                                 fPos->GetUParameter(), fPos->GetVParameter() );
12580     break;
12581   }
12582   case SMDS_TOP_EDGE:
12583   {
12584     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12585     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12586     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12587     break;
12588   }
12589   case SMDS_TOP_VERTEX:
12590   {
12591     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12592     break;
12593   }
12594   case SMDS_TOP_UNSPEC:
12595   default:;
12596   }
12597 }