Salome HOME
Mesh Offset failure
[modules/smesh.git] / src / SMESH / SMESH_MeshEditor.cxx
1 // Copyright (C) 2007-2016  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_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
48
49 #include <Basics_OCCTVersion.hxx>
50
51 #include "utilities.h"
52 #include "chrono.hxx"
53
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
58 #include <ElCLib.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
69 #include <TopExp.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
74 #include <TopoDS.hxx>
75 #include <TopoDS_Edge.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 //=======================================================================
108 //function : SMESH_MeshEditor
109 //purpose  :
110 //=======================================================================
111
112 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
113   :myMesh( theMesh ) // theMesh may be NULL
114 {
115 }
116
117 //================================================================================
118 /*!
119  * \brief Return mesh DS
120  */
121 //================================================================================
122
123 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
124 {
125   return myMesh->GetMeshDS();
126 }
127
128
129 //================================================================================
130 /*!
131  * \brief Clears myLastCreatedNodes and myLastCreatedElems
132  */
133 //================================================================================
134
135 void SMESH_MeshEditor::ClearLastCreated()
136 {
137   SMESHUtils::FreeVector( myLastCreatedElems );
138   SMESHUtils::FreeVector( myLastCreatedNodes );
139 }
140
141 //================================================================================
142 /*!
143  * \brief Initializes members by an existing element
144  *  \param [in] elem - the source element
145  *  \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
146  */
147 //================================================================================
148
149 SMESH_MeshEditor::ElemFeatures&
150 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
151 {
152   if ( elem )
153   {
154     myType = elem->GetType();
155     if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
156     {
157       myIsPoly = elem->IsPoly();
158       if ( myIsPoly )
159       {
160         myIsQuad = elem->IsQuadratic();
161         if ( myType == SMDSAbs_Volume && !basicOnly )
162         {
163           vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
164           myPolyhedQuantities.swap( quant );
165         }
166       }
167     }
168     else if ( myType == SMDSAbs_Ball && !basicOnly )
169     {
170       myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
171     }
172   }
173   return *this;
174 }
175
176 //=======================================================================
177 /*!
178  * \brief Add element
179  */
180 //=======================================================================
181
182 SMDS_MeshElement*
183 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
184                              const ElemFeatures&                  features)
185 {
186   SMDS_MeshElement* e = 0;
187   int nbnode = node.size();
188   SMESHDS_Mesh* mesh = GetMeshDS();
189   const int ID = features.myID;
190
191   switch ( features.myType ) {
192   case SMDSAbs_Face:
193     if ( !features.myIsPoly ) {
194       if      (nbnode == 3) {
195         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
196         else           e = mesh->AddFace      (node[0], node[1], node[2] );
197       }
198       else if (nbnode == 4) {
199         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
200         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3] );
201       }
202       else if (nbnode == 6) {
203         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
204                                                node[4], node[5], ID);
205         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
206                                                node[4], node[5] );
207       }
208       else if (nbnode == 7) {
209         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
210                                                node[4], node[5], node[6], ID);
211         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
212                                                node[4], node[5], node[6] );
213       }
214       else if (nbnode == 8) {
215         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216                                                node[4], node[5], node[6], node[7], ID);
217         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
218                                                node[4], node[5], node[6], node[7] );
219       }
220       else if (nbnode == 9) {
221         if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222                                                node[4], node[5], node[6], node[7], node[8], ID);
223         else           e = mesh->AddFace      (node[0], node[1], node[2], node[3],
224                                                node[4], node[5], node[6], node[7], node[8] );
225       }
226     }
227     else if ( !features.myIsQuad )
228     {
229       if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
230       else           e = mesh->AddPolygonalFace      (node    );
231     }
232     else if ( nbnode % 2 == 0 ) // just a protection
233     {
234       if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
235       else           e = mesh->AddQuadPolygonalFace      (node    );
236     }
237     break;
238
239   case SMDSAbs_Volume:
240     if ( !features.myIsPoly ) {
241       if      (nbnode == 4) {
242         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
243         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3] );
244       }
245       else if (nbnode == 5) {
246         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247                                                  node[4], ID);
248         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
249                                                  node[4] );
250       }
251       else if (nbnode == 6) {
252         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
253                                                  node[4], node[5], ID);
254         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
255                                                  node[4], node[5] );
256       }
257       else if (nbnode == 8) {
258         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259                                                  node[4], node[5], node[6], node[7], ID);
260         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
261                                                  node[4], node[5], node[6], node[7] );
262       }
263       else if (nbnode == 10) {
264         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265                                                  node[4], node[5], node[6], node[7],
266                                                  node[8], node[9], ID);
267         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
268                                                  node[4], node[5], node[6], node[7],
269                                                  node[8], node[9] );
270       }
271       else if (nbnode == 12) {
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], ID);
275         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
276                                                  node[4], node[5], node[6], node[7],
277                                                  node[8], node[9], node[10], node[11] );
278       }
279       else if (nbnode == 13) {
280         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
281                                                  node[4], node[5], node[6], node[7],
282                                                  node[8], node[9], node[10],node[11],
283                                                  node[12],ID);
284         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
285                                                  node[4], node[5], node[6], node[7],
286                                                  node[8], node[9], node[10],node[11],
287                                                  node[12] );
288       }
289       else if (nbnode == 15) {
290         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
291                                                  node[4], node[5], node[6], node[7],
292                                                  node[8], node[9], node[10],node[11],
293                                                  node[12],node[13],node[14],ID);
294         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
295                                                  node[4], node[5], node[6], node[7],
296                                                  node[8], node[9], node[10],node[11],
297                                                  node[12],node[13],node[14] );
298       }
299       else if (nbnode == 20) {
300         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
301                                                  node[4], node[5], node[6], node[7],
302                                                  node[8], node[9], node[10],node[11],
303                                                  node[12],node[13],node[14],node[15],
304                                                  node[16],node[17],node[18],node[19],ID);
305         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
306                                                  node[4], node[5], node[6], node[7],
307                                                  node[8], node[9], node[10],node[11],
308                                                  node[12],node[13],node[14],node[15],
309                                                  node[16],node[17],node[18],node[19] );
310       }
311       else if (nbnode == 27) {
312         if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313                                                  node[4], node[5], node[6], node[7],
314                                                  node[8], node[9], node[10],node[11],
315                                                  node[12],node[13],node[14],node[15],
316                                                  node[16],node[17],node[18],node[19],
317                                                  node[20],node[21],node[22],node[23],
318                                                  node[24],node[25],node[26], ID);
319         else           e = mesh->AddVolume      (node[0], node[1], node[2], node[3],
320                                                  node[4], node[5], node[6], node[7],
321                                                  node[8], node[9], node[10],node[11],
322                                                  node[12],node[13],node[14],node[15],
323                                                  node[16],node[17],node[18],node[19],
324                                                  node[20],node[21],node[22],node[23],
325                                                  node[24],node[25],node[26] );
326       }
327     }
328     else if ( !features.myIsQuad )
329     {
330       if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
331       else           e = mesh->AddPolyhedralVolume      (node, features.myPolyhedQuantities    );
332     }
333     else
334     {
335       // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
336       // else           e = mesh->AddQuadPolyhedralVolume      (node, features.myPolyhedQuantities   );
337     }
338     break;
339
340   case SMDSAbs_Edge:
341     if ( nbnode == 2 ) {
342       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
343       else           e = mesh->AddEdge      (node[0], node[1] );
344     }
345     else if ( nbnode == 3 ) {
346       if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
347       else           e = mesh->AddEdge      (node[0], node[1], node[2] );
348     }
349     break;
350
351   case SMDSAbs_0DElement:
352     if ( nbnode == 1 ) {
353       if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
354       else           e = mesh->Add0DElement      (node[0] );
355     }
356     break;
357
358   case SMDSAbs_Node:
359     if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
360     else           e = mesh->AddNode      (node[0]->X(), node[0]->Y(), node[0]->Z()    );
361     break;
362
363   case SMDSAbs_Ball:
364     if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
365     else           e = mesh->AddBall      (node[0], features.myBallDiameter    );
366     break;
367
368   default:;
369   }
370   if ( e ) myLastCreatedElems.push_back( e );
371   return e;
372 }
373
374 //=======================================================================
375 /*!
376  * \brief Add element
377  */
378 //=======================================================================
379
380 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
381                                                const ElemFeatures& features)
382 {
383   vector<const SMDS_MeshNode*> nodes;
384   nodes.reserve( nodeIDs.size() );
385   vector<int>::const_iterator id = nodeIDs.begin();
386   while ( id != nodeIDs.end() ) {
387     if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
388       nodes.push_back( node );
389     else
390       return 0;
391   }
392   return AddElement( nodes, features );
393 }
394
395 //=======================================================================
396 //function : Remove
397 //purpose  : Remove a node or an element.
398 //           Modify a compute state of sub-meshes which become empty
399 //=======================================================================
400
401 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
402                               const bool         isNodes )
403 {
404   ClearLastCreated();
405
406   SMESHDS_Mesh* aMesh = GetMeshDS();
407   set< SMESH_subMesh *> smmap;
408
409   int removed = 0;
410   list<int>::const_iterator it = theIDs.begin();
411   for ( ; it != theIDs.end(); it++ ) {
412     const SMDS_MeshElement * elem;
413     if ( isNodes )
414       elem = aMesh->FindNode( *it );
415     else
416       elem = aMesh->FindElement( *it );
417     if ( !elem )
418       continue;
419
420     // Notify VERTEX sub-meshes about modification
421     if ( isNodes ) {
422       const SMDS_MeshNode* node = cast2Node( elem );
423       if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424         if ( int aShapeID = node->getshapeId() )
425           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
426             smmap.insert( sm );
427     }
428     // Find sub-meshes to notify about modification
429     //     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430     //     while ( nodeIt->more() ) {
431     //       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432     //       const SMDS_PositionPtr& aPosition = node->GetPosition();
433     //       if ( aPosition.get() ) {
434     //         if ( int aShapeID = aPosition->GetShapeId() ) {
435     //           if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436     //             smmap.insert( sm );
437     //         }
438     //       }
439     //     }
440
441     // Do remove
442     if ( isNodes )
443       aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
444     else
445       aMesh->RemoveElement( elem );
446     removed++;
447   }
448
449   // Notify sub-meshes about modification
450   if ( !smmap.empty() ) {
451     set< SMESH_subMesh *>::iterator smIt;
452     for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453       (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
454   }
455
456   //   // Check if the whole mesh becomes empty
457   //   if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458   //     sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
459
460   return removed;
461 }
462
463 //================================================================================
464 /*!
465  * \brief Create 0D elements on all nodes of the given object.
466  *  \param elements - Elements on whose nodes to create 0D elements; if empty, 
467  *                    the all mesh is treated
468  *  \param all0DElems - returns all 0D elements found or created on nodes of \a elements
469  *  \param duplicateElements - to add one more 0D element to a node or not
470  */
471 //================================================================================
472
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474                                                    TIDSortedElemSet&       all0DElems,
475                                                    const bool              duplicateElements )
476 {
477   SMDS_ElemIteratorPtr elemIt;
478   if ( elements.empty() )
479   {
480     elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
481   }
482   else
483   {
484     elemIt = SMESHUtils::elemSetIterator( elements );
485   }
486
487   while ( elemIt->more() )
488   {
489     const SMDS_MeshElement* e = elemIt->next();
490     SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
491     while ( nodeIt->more() )
492     {
493       const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
494       SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
495       if ( duplicateElements || !it0D->more() )
496       {
497         myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
498         all0DElems.insert( myLastCreatedElems.back() );
499       }
500       while ( it0D->more() )
501         all0DElems.insert( it0D->next() );
502     }
503   }
504 }
505
506 //=======================================================================
507 //function : FindShape
508 //purpose  : Return an index of the shape theElem is on
509 //           or zero if a shape not found
510 //=======================================================================
511
512 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
513 {
514   ClearLastCreated();
515
516   SMESHDS_Mesh * aMesh = GetMeshDS();
517   if ( aMesh->ShapeToMesh().IsNull() )
518     return 0;
519
520   int aShapeID = theElem->getshapeId();
521   if ( aShapeID < 1 )
522     return 0;
523
524   if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
525     if ( sm->Contains( theElem ))
526       return aShapeID;
527
528   if ( theElem->GetType() == SMDSAbs_Node ) {
529     MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
530   }
531   else {
532     MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
533   }
534
535   TopoDS_Shape aShape; // the shape a node of theElem is on
536   if ( theElem->GetType() != SMDSAbs_Node )
537   {
538     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
539     while ( nodeIt->more() ) {
540       const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
541       if ((aShapeID = node->getshapeId()) > 0) {
542         if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
543           if ( sm->Contains( theElem ))
544             return aShapeID;
545           if ( aShape.IsNull() )
546             aShape = aMesh->IndexToShape( aShapeID );
547         }
548       }
549     }
550   }
551
552   // None of nodes is on a proper shape,
553   // find the shape among ancestors of aShape on which a node is
554   if ( !aShape.IsNull() ) {
555     TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
556     for ( ; ancIt.More(); ancIt.Next() ) {
557       SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
558       if ( sm && sm->Contains( theElem ))
559         return aMesh->ShapeToIndex( ancIt.Value() );
560     }
561   }
562   else
563   {
564     SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
565     while ( const SMESHDS_SubMesh* sm = smIt->next() )
566       if ( sm->Contains( theElem ))
567         return sm->GetID();
568   }
569
570   return 0;
571 }
572
573 //=======================================================================
574 //function : IsMedium
575 //purpose  :
576 //=======================================================================
577
578 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode*      node,
579                                 const SMDSAbs_ElementType typeToCheck)
580 {
581   bool isMedium = false;
582   SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
583   while (it->more() && !isMedium ) {
584     const SMDS_MeshElement* elem = it->next();
585     isMedium = elem->IsMediumNode(node);
586   }
587   return isMedium;
588 }
589
590 //=======================================================================
591 //function : shiftNodesQuadTria
592 //purpose  : Shift nodes in the array corresponded to quadratic triangle
593 //           example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
594 //=======================================================================
595
596 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
597 {
598   const SMDS_MeshNode* nd1 = aNodes[0];
599   aNodes[0] = aNodes[1];
600   aNodes[1] = aNodes[2];
601   aNodes[2] = nd1;
602   const SMDS_MeshNode* nd2 = aNodes[3];
603   aNodes[3] = aNodes[4];
604   aNodes[4] = aNodes[5];
605   aNodes[5] = nd2;
606 }
607
608 //=======================================================================
609 //function : nbEdgeConnectivity
610 //purpose  : return number of the edges connected with the theNode.
611 //           if theEdges has connections with the other type of the
612 //           elements, return -1
613 //=======================================================================
614
615 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
616 {
617   // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
618   // int nb=0;
619   // while(elemIt->more()) {
620   //   elemIt->next();
621   //   nb++;
622   // }
623   // return nb;
624   return theNode->NbInverseElements();
625 }
626
627 //=======================================================================
628 //function : getNodesFromTwoTria
629 //purpose  : 
630 //=======================================================================
631
632 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
633                                 const SMDS_MeshElement * theTria2,
634                                 vector< const SMDS_MeshNode*>& N1,
635                                 vector< const SMDS_MeshNode*>& N2)
636 {
637   N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
638   if ( N1.size() < 6 ) return false;
639   N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
640   if ( N2.size() < 6 ) return false;
641
642   int sames[3] = {-1,-1,-1};
643   int nbsames = 0;
644   int i, j;
645   for(i=0; i<3; i++) {
646     for(j=0; j<3; j++) {
647       if(N1[i]==N2[j]) {
648         sames[i] = j;
649         nbsames++;
650         break;
651       }
652     }
653   }
654   if(nbsames!=2) return false;
655   if(sames[0]>-1) {
656     shiftNodesQuadTria(N1);
657     if(sames[1]>-1) {
658       shiftNodesQuadTria(N1);
659     }
660   }
661   i = sames[0] + sames[1] + sames[2];
662   for(; i<2; i++) {
663     shiftNodesQuadTria(N2);
664   }
665   // now we receive following N1 and N2 (using numeration as in the image below)
666   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
667   // i.e. first nodes from both arrays form a new diagonal
668   return true;
669 }
670
671 //=======================================================================
672 //function : InverseDiag
673 //purpose  : Replace two neighbour triangles with ones built on the same 4 nodes
674 //           but having other common link.
675 //           Return False if args are improper
676 //=======================================================================
677
678 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
679                                     const SMDS_MeshElement * theTria2 )
680 {
681   ClearLastCreated();
682
683   if (!theTria1 || !theTria2)
684     return false;
685
686   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
687   if (!F1) return false;
688   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
689   if (!F2) return false;
690   if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
691       (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
692
693     //  1 +--+ A  theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
694     //    | /|    theTria2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
695     //    |/ |                                         | \|
696     //  B +--+ 2                                     B +--+ 2
697
698     // put nodes in array and find out indices of the same ones
699     const SMDS_MeshNode* aNodes [6];
700     int sameInd [] = { -1, -1, -1, -1, -1, -1 };
701     int i = 0;
702     SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
703     while ( it->more() ) {
704       aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
705
706       if ( i > 2 ) // theTria2
707         // find same node of theTria1
708         for ( int j = 0; j < 3; j++ )
709           if ( aNodes[ i ] == aNodes[ j ]) {
710             sameInd[ j ] = i;
711             sameInd[ i ] = j;
712             break;
713           }
714       // next
715       i++;
716       if ( i == 3 ) {
717         if ( it->more() )
718           return false; // theTria1 is not a triangle
719         it = theTria2->nodesIterator();
720       }
721       if ( i == 6 && it->more() )
722         return false; // theTria2 is not a triangle
723     }
724
725     // find indices of 1,2 and of A,B in theTria1
726     int iA = -1, iB = 0, i1 = 0, i2 = 0;
727     for ( i = 0; i < 6; i++ ) {
728       if ( sameInd [ i ] == -1 ) {
729         if ( i < 3 ) i1 = i;
730         else         i2 = i;
731       }
732       else if (i < 3) {
733         if ( iA >= 0) iB = i;
734         else          iA = i;
735       }
736     }
737     // nodes 1 and 2 should not be the same
738     if ( aNodes[ i1 ] == aNodes[ i2 ] )
739       return false;
740
741     // theTria1: A->2
742     aNodes[ iA ] = aNodes[ i2 ];
743     // theTria2: B->1
744     aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
745
746     GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
747     GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
748
749     return true;
750
751   } // end if(F1 && F2)
752
753   // check case of quadratic faces
754   if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
755       theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
756     return false;
757   if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
758       theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
759     return false;
760
761   //       5
762   //  1 +--+--+ 2  theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
763   //    |    /|    theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
764   //    |   / |
765   //  7 +  +  + 6
766   //    | /9  |
767   //    |/    |
768   //  4 +--+--+ 3
769   //       8
770
771   vector< const SMDS_MeshNode* > N1;
772   vector< const SMDS_MeshNode* > N2;
773   if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
774     return false;
775   // now we receive following N1 and N2 (using numeration as above image)
776   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
777   // i.e. first nodes from both arrays determ new diagonal
778
779   vector< const SMDS_MeshNode*> N1new( N1.size() );
780   vector< const SMDS_MeshNode*> N2new( N2.size() );
781   N1new.back() = N1.back(); // central node of biquadratic
782   N2new.back() = N2.back();
783   N1new[0] = N1[0];  N2new[0] = N1[0];
784   N1new[1] = N2[0];  N2new[1] = N1[1];
785   N1new[2] = N2[1];  N2new[2] = N2[0];
786   N1new[3] = N1[4];  N2new[3] = N1[3];
787   N1new[4] = N2[3];  N2new[4] = N2[5];
788   N1new[5] = N1[5];  N2new[5] = N1[4];
789   // change nodes in faces
790   GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
791   GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
792
793   // move the central node of biquadratic triangle
794   SMESH_MesherHelper helper( *GetMesh() );
795   for ( int is2nd = 0; is2nd < 2; ++is2nd )
796   {
797     const SMDS_MeshElement*         tria = is2nd ? theTria2 : theTria1;
798     vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
799     if ( nodes.size() < 7 )
800       continue;
801     helper.SetSubShape( tria->getshapeId() );
802     const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
803     gp_Pnt xyz;
804     if ( F.IsNull() )
805     {
806       xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
807               SMESH_TNodeXYZ( nodes[4] ) +
808               SMESH_TNodeXYZ( nodes[5] )) / 3.;
809     }
810     else
811     {
812       bool checkUV;
813       gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
814                    helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
815                    helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
816       TopLoc_Location loc;
817       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
818       xyz = S->Value( uv.X(), uv.Y() );
819       xyz.Transform( loc );
820       if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE &&  // set UV
821            nodes[6]->getshapeId() > 0 )
822         GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
823     }
824     GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
825   }
826   return true;
827 }
828
829 //=======================================================================
830 //function : findTriangles
831 //purpose  : find triangles sharing theNode1-theNode2 link
832 //=======================================================================
833
834 static bool findTriangles(const SMDS_MeshNode *    theNode1,
835                           const SMDS_MeshNode *    theNode2,
836                           const SMDS_MeshElement*& theTria1,
837                           const SMDS_MeshElement*& theTria2)
838 {
839   if ( !theNode1 || !theNode2 ) return false;
840
841   theTria1 = theTria2 = 0;
842
843   set< const SMDS_MeshElement* > emap;
844   SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
845   while (it->more()) {
846     const SMDS_MeshElement* elem = it->next();
847     if ( elem->NbCornerNodes() == 3 )
848       emap.insert( elem );
849   }
850   it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
851   while (it->more()) {
852     const SMDS_MeshElement* elem = it->next();
853     if ( emap.count( elem )) {
854       if ( !theTria1 )
855       {
856         theTria1 = elem;
857       }
858       else  
859       {
860         theTria2 = elem;
861         // theTria1 must be element with minimum ID
862         if ( theTria2->GetID() < theTria1->GetID() )
863           std::swap( theTria2, theTria1 );
864         return true;
865       }
866     }
867   }
868   return false;
869 }
870
871 //=======================================================================
872 //function : InverseDiag
873 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
874 //           with ones built on the same 4 nodes but having other common link.
875 //           Return false if proper faces not found
876 //=======================================================================
877
878 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
879                                     const SMDS_MeshNode * theNode2)
880 {
881   ClearLastCreated();
882
883   const SMDS_MeshElement *tr1, *tr2;
884   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
885     return false;
886
887   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
888   if (!F1) return false;
889   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
890   if (!F2) return false;
891   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
892       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
893
894     //  1 +--+ A  tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
895     //    | /|    tr2: ( B A 2 ) B->1 ( 1 A 2 )   |\ |
896     //    |/ |                                    | \|
897     //  B +--+ 2                                B +--+ 2
898
899     // put nodes in array
900     // and find indices of 1,2 and of A in tr1 and of B in tr2
901     int i, iA1 = 0, i1 = 0;
902     const SMDS_MeshNode* aNodes1 [3];
903     SMDS_ElemIteratorPtr it;
904     for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
905       aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
906       if ( aNodes1[ i ] == theNode1 )
907         iA1 = i; // node A in tr1
908       else if ( aNodes1[ i ] != theNode2 )
909         i1 = i;  // node 1
910     }
911     int iB2 = 0, i2 = 0;
912     const SMDS_MeshNode* aNodes2 [3];
913     for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
914       aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
915       if ( aNodes2[ i ] == theNode2 )
916         iB2 = i; // node B in tr2
917       else if ( aNodes2[ i ] != theNode1 )
918         i2 = i;  // node 2
919     }
920
921     // nodes 1 and 2 should not be the same
922     if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
923       return false;
924
925     // tr1: A->2
926     aNodes1[ iA1 ] = aNodes2[ i2 ];
927     // tr2: B->1
928     aNodes2[ iB2 ] = aNodes1[ i1 ];
929
930     GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
931     GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
932
933     return true;
934   }
935
936   // check case of quadratic faces
937   return InverseDiag(tr1,tr2);
938 }
939
940 //=======================================================================
941 //function : getQuadrangleNodes
942 //purpose  : fill theQuadNodes - nodes of a quadrangle resulting from
943 //           fusion of triangles tr1 and tr2 having shared link on
944 //           theNode1 and theNode2
945 //=======================================================================
946
947 bool getQuadrangleNodes(const SMDS_MeshNode *    theQuadNodes [],
948                         const SMDS_MeshNode *    theNode1,
949                         const SMDS_MeshNode *    theNode2,
950                         const SMDS_MeshElement * tr1,
951                         const SMDS_MeshElement * tr2 )
952 {
953   if( tr1->NbNodes() != tr2->NbNodes() )
954     return false;
955   // find the 4-th node to insert into tr1
956   const SMDS_MeshNode* n4 = 0;
957   SMDS_ElemIteratorPtr it = tr2->nodesIterator();
958   int i=0;
959   while ( !n4 && i<3 ) {
960     const SMDS_MeshNode * n = cast2Node( it->next() );
961     i++;
962     bool isDiag = ( n == theNode1 || n == theNode2 );
963     if ( !isDiag )
964       n4 = n;
965   }
966   // Make an array of nodes to be in a quadrangle
967   int iNode = 0, iFirstDiag = -1;
968   it = tr1->nodesIterator();
969   i=0;
970   while ( i<3 ) {
971     const SMDS_MeshNode * n = cast2Node( it->next() );
972     i++;
973     bool isDiag = ( n == theNode1 || n == theNode2 );
974     if ( isDiag ) {
975       if ( iFirstDiag < 0 )
976         iFirstDiag = iNode;
977       else if ( iNode - iFirstDiag == 1 )
978         theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
979     }
980     else if ( n == n4 ) {
981       return false; // tr1 and tr2 should not have all the same nodes
982     }
983     theQuadNodes[ iNode++ ] = n;
984   }
985   if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
986     theQuadNodes[ iNode ] = n4;
987
988   return true;
989 }
990
991 //=======================================================================
992 //function : DeleteDiag
993 //purpose  : Replace two neighbour triangles sharing theNode1-theNode2 link
994 //           with a quadrangle built on the same 4 nodes.
995 //           Return false if proper faces not found
996 //=======================================================================
997
998 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
999                                    const SMDS_MeshNode * theNode2)
1000 {
1001   ClearLastCreated();
1002
1003   const SMDS_MeshElement *tr1, *tr2;
1004   if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1005     return false;
1006
1007   const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1008   if (!F1) return false;
1009   const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1010   if (!F2) return false;
1011   SMESHDS_Mesh * aMesh = GetMeshDS();
1012
1013   if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1014       (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1015
1016     const SMDS_MeshNode* aNodes [ 4 ];
1017     if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1018       return false;
1019
1020     const SMDS_MeshElement* newElem = 0;
1021     newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1022     myLastCreatedElems.push_back(newElem);
1023     AddToSameGroups( newElem, tr1, aMesh );
1024     int aShapeId = tr1->getshapeId();
1025     if ( aShapeId )
1026     {
1027       aMesh->SetMeshElementOnShape( newElem, aShapeId );
1028     }
1029     aMesh->RemoveElement( tr1 );
1030     aMesh->RemoveElement( tr2 );
1031
1032     return true;
1033   }
1034
1035   // check case of quadratic faces
1036   if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1037     return false;
1038   if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1039     return false;
1040
1041   //       5
1042   //  1 +--+--+ 2  tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1043   //    |    /|    tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1044   //    |   / |
1045   //  7 +  +  + 6
1046   //    | /9  |
1047   //    |/    |
1048   //  4 +--+--+ 3
1049   //       8
1050
1051   vector< const SMDS_MeshNode* > N1;
1052   vector< const SMDS_MeshNode* > N2;
1053   if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1054     return false;
1055   // now we receive following N1 and N2 (using numeration as above image)
1056   // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
1057   // i.e. first nodes from both arrays determ new diagonal
1058
1059   const SMDS_MeshNode* aNodes[8];
1060   aNodes[0] = N1[0];
1061   aNodes[1] = N1[1];
1062   aNodes[2] = N2[0];
1063   aNodes[3] = N2[1];
1064   aNodes[4] = N1[3];
1065   aNodes[5] = N2[5];
1066   aNodes[6] = N2[3];
1067   aNodes[7] = N1[5];
1068
1069   const SMDS_MeshElement* newElem = 0;
1070   newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1071                             aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1072   myLastCreatedElems.push_back(newElem);
1073   AddToSameGroups( newElem, tr1, aMesh );
1074   int aShapeId = tr1->getshapeId();
1075   if ( aShapeId )
1076   {
1077     aMesh->SetMeshElementOnShape( newElem, aShapeId );
1078   }
1079   aMesh->RemoveElement( tr1 );
1080   aMesh->RemoveElement( tr2 );
1081
1082   // remove middle node (9)
1083   GetMeshDS()->RemoveNode( N1[4] );
1084
1085   return true;
1086 }
1087
1088 //=======================================================================
1089 //function : Reorient
1090 //purpose  : Reverse theElement orientation
1091 //=======================================================================
1092
1093 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1094 {
1095   ClearLastCreated();
1096
1097   if (!theElem)
1098     return false;
1099   SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1100   if ( !it || !it->more() )
1101     return false;
1102
1103   const SMDSAbs_ElementType type = theElem->GetType();
1104   if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1105     return false;
1106
1107   const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1108   if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1109   {
1110     const SMDS_VtkVolume* aPolyedre =
1111       dynamic_cast<const SMDS_VtkVolume*>( theElem );
1112     if (!aPolyedre) {
1113       MESSAGE("Warning: bad volumic element");
1114       return false;
1115     }
1116     const int nbFaces = aPolyedre->NbFaces();
1117     vector<const SMDS_MeshNode *> poly_nodes;
1118     vector<int> quantities (nbFaces);
1119
1120     // reverse each face of the polyedre
1121     for (int iface = 1; iface <= nbFaces; iface++) {
1122       int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1123       quantities[iface - 1] = nbFaceNodes;
1124
1125       for (inode = nbFaceNodes; inode >= 1; inode--) {
1126         const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1127         poly_nodes.push_back(curNode);
1128       }
1129     }
1130     return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1131   }
1132   else // other elements
1133   {
1134     vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1135     const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1136     if ( interlace.empty() )
1137     {
1138       std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1139     }
1140     else
1141     {
1142       SMDS_MeshCell::applyInterlace( interlace, nodes );
1143     }
1144     return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1145   }
1146   return false;
1147 }
1148
1149 //================================================================================
1150 /*!
1151  * \brief Reorient faces.
1152  * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1153  * \param theDirection - desired direction of normal of \a theFace
1154  * \param theFace - one of \a theFaces that sould be oriented according to
1155  *        \a theDirection and whose orientation defines orientation of other faces
1156  * \return number of reoriented faces.
1157  */
1158 //================================================================================
1159
1160 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet &       theFaces,
1161                                   const gp_Dir&            theDirection,
1162                                   const SMDS_MeshElement * theFace)
1163 {
1164   int nbReori = 0;
1165   if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1166
1167   if ( theFaces.empty() )
1168   {
1169     SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1170     while ( fIt->more() )
1171       theFaces.insert( theFaces.end(), fIt->next() );
1172   }
1173
1174   // orient theFace according to theDirection
1175   gp_XYZ normal;
1176   SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1177   if ( normal * theDirection.XYZ() < 0 )
1178     nbReori += Reorient( theFace );
1179
1180   // Orient other faces
1181
1182   set< const SMDS_MeshElement* > startFaces, visitedFaces;
1183   TIDSortedElemSet avoidSet;
1184   set< SMESH_TLink > checkedLinks;
1185   pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1186
1187   if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1188     theFaces.erase( theFace );
1189   startFaces.insert( theFace );
1190
1191   int nodeInd1, nodeInd2;
1192   const SMDS_MeshElement*           otherFace;
1193   vector< const SMDS_MeshElement* > facesNearLink;
1194   vector< std::pair< int, int > >   nodeIndsOfFace;
1195
1196   set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1197   while ( !startFaces.empty() )
1198   {
1199     startFace = startFaces.begin();
1200     theFace = *startFace;
1201     startFaces.erase( startFace );
1202     if ( !visitedFaces.insert( theFace ).second )
1203       continue;
1204
1205     avoidSet.clear();
1206     avoidSet.insert(theFace);
1207
1208     NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1209
1210     const int nbNodes = theFace->NbCornerNodes();
1211     for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1212     {
1213       link.second = theFace->GetNode(( i+1 ) % nbNodes );
1214       linkIt_isNew = checkedLinks.insert( link );
1215       if ( !linkIt_isNew.second )
1216       {
1217         // link has already been checked and won't be encountered more
1218         // if the group (theFaces) is manifold
1219         //checkedLinks.erase( linkIt_isNew.first );
1220       }
1221       else
1222       {
1223         facesNearLink.clear();
1224         nodeIndsOfFace.clear();
1225         while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1226                                                              theFaces, avoidSet,
1227                                                              &nodeInd1, &nodeInd2 )))
1228           if ( otherFace != theFace)
1229           {
1230             facesNearLink.push_back( otherFace );
1231             nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1232             avoidSet.insert( otherFace );
1233           }
1234         if ( facesNearLink.size() > 1 )
1235         {
1236           // NON-MANIFOLD mesh shell !
1237           // select a face most co-directed with theFace,
1238           // other faces won't be visited this time
1239           gp_XYZ NF, NOF;
1240           SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1241           double proj, maxProj = -1;
1242           for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1243             SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1244             if (( proj = Abs( NF * NOF )) > maxProj ) {
1245               maxProj = proj;
1246               otherFace = facesNearLink[i];
1247               nodeInd1  = nodeIndsOfFace[i].first;
1248               nodeInd2  = nodeIndsOfFace[i].second;
1249             }
1250           }
1251           // not to visit rejected faces
1252           for ( size_t i = 0; i < facesNearLink.size(); ++i )
1253             if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1254               visitedFaces.insert( facesNearLink[i] );
1255         }
1256         else if ( facesNearLink.size() == 1 )
1257         {
1258           otherFace = facesNearLink[0];
1259           nodeInd1  = nodeIndsOfFace.back().first;
1260           nodeInd2  = nodeIndsOfFace.back().second;
1261         }
1262         if ( otherFace && otherFace != theFace)
1263         {
1264           // link must be reverse in otherFace if orientation ot otherFace
1265           // is same as that of theFace
1266           if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1267           {
1268             nbReori += Reorient( otherFace );
1269           }
1270           startFaces.insert( otherFace );
1271         }
1272       }
1273       std::swap( link.first, link.second ); // reverse the link
1274     }
1275   }
1276   return nbReori;
1277 }
1278
1279 //================================================================================
1280 /*!
1281  * \brief Reorient faces basing on orientation of adjacent volumes.
1282  * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1283  * \param theVolumes - reference volumes.
1284  * \param theOutsideNormal - to orient faces to have their normal
1285  *        pointing either \a outside or \a inside the adjacent volumes.
1286  * \return number of reoriented faces.
1287  */
1288 //================================================================================
1289
1290 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1291                                       TIDSortedElemSet & theVolumes,
1292                                       const bool         theOutsideNormal)
1293 {
1294   int nbReori = 0;
1295
1296   SMDS_ElemIteratorPtr faceIt;
1297   if ( theFaces.empty() )
1298     faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1299   else
1300     faceIt = SMESHUtils::elemSetIterator( theFaces );
1301
1302   vector< const SMDS_MeshNode* > faceNodes;
1303   TIDSortedElemSet checkedVolumes;
1304   set< const SMDS_MeshNode* > faceNodesSet;
1305   SMDS_VolumeTool volumeTool;
1306
1307   while ( faceIt->more() ) // loop on given faces
1308   {
1309     const SMDS_MeshElement* face = faceIt->next();
1310     if ( face->GetType() != SMDSAbs_Face )
1311       continue;
1312
1313     const size_t nbCornersNodes = face->NbCornerNodes();
1314     faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1315
1316     checkedVolumes.clear();
1317     SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1318     while ( vIt->more() )
1319     {
1320       const SMDS_MeshElement* volume = vIt->next();
1321
1322       if ( !checkedVolumes.insert( volume ).second )
1323         continue;
1324       if ( !theVolumes.empty() && !theVolumes.count( volume ))
1325         continue;
1326
1327       // is volume adjacent?
1328       bool allNodesCommon = true;
1329       for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1330         allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1331       if ( !allNodesCommon )
1332         continue;
1333
1334       // get nodes of a corresponding volume facet
1335       faceNodesSet.clear();
1336       faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1337       volumeTool.Set( volume );
1338       int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1339       if ( facetID < 0 ) continue;
1340       volumeTool.SetExternalNormal();
1341       const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1342
1343       // compare order of faceNodes and facetNodes
1344       const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1345       int iNN[2];
1346       for ( int i = 0; i < 2; ++i )
1347       {
1348         const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1349         for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1350           if ( faceNodes[ iN ] == n )
1351           {
1352             iNN[ i ] = iN;
1353             break;
1354           }
1355       }
1356       bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1357       if ( isOutside != theOutsideNormal )
1358         nbReori += Reorient( face );
1359     }
1360   }  // loop on given faces
1361
1362   return nbReori;
1363 }
1364
1365 //=======================================================================
1366 //function : getBadRate
1367 //purpose  :
1368 //=======================================================================
1369
1370 static double getBadRate (const SMDS_MeshElement*               theElem,
1371                           SMESH::Controls::NumericalFunctorPtr& theCrit)
1372 {
1373   SMESH::Controls::TSequenceOfXYZ P;
1374   if ( !theElem || !theCrit->GetPoints( theElem, P ))
1375     return 1e100;
1376   return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1377   //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1378 }
1379
1380 //=======================================================================
1381 //function : QuadToTri
1382 //purpose  : Cut quadrangles into triangles.
1383 //           theCrit is used to select a diagonal to cut
1384 //=======================================================================
1385
1386 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet &                   theElems,
1387                                   SMESH::Controls::NumericalFunctorPtr theCrit)
1388 {
1389   ClearLastCreated();
1390
1391   if ( !theCrit.get() )
1392     return false;
1393
1394   SMESHDS_Mesh *       aMesh = GetMeshDS();
1395   Handle(Geom_Surface) surface;
1396   SMESH_MesherHelper   helper( *GetMesh() );
1397
1398   myLastCreatedElems.reserve( theElems.size() * 2 );
1399
1400   TIDSortedElemSet::iterator itElem;
1401   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1402   {
1403     const SMDS_MeshElement* elem = *itElem;
1404     if ( !elem || elem->GetType() != SMDSAbs_Face )
1405       continue;
1406     if ( elem->NbCornerNodes() != 4 )
1407       continue;
1408
1409     // retrieve element nodes
1410     vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1411
1412     // compare two sets of possible triangles
1413     double aBadRate1, aBadRate2; // to what extent a set is bad
1414     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1415     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1416     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1417
1418     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1419     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1420     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1421
1422     const int aShapeId = FindShape( elem );
1423     const SMDS_MeshElement* newElem1 = 0;
1424     const SMDS_MeshElement* newElem2 = 0;
1425
1426     if ( !elem->IsQuadratic() ) // split liner quadrangle
1427     {
1428       // for MaxElementLength2D functor we return minimum diagonal for splitting,
1429       // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1430       if ( aBadRate1 <= aBadRate2 ) {
1431         // tr1 + tr2 is better
1432         newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1433         newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1434       }
1435       else {
1436         // tr3 + tr4 is better
1437         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1438         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1439       }
1440     }
1441     else // split quadratic quadrangle
1442     {
1443       helper.SetIsQuadratic( true );
1444       helper.SetIsBiQuadratic( aNodes.size() == 9 );
1445
1446       helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1447       if ( aNodes.size() == 9 )
1448       {
1449         helper.SetIsBiQuadratic( true );
1450         if ( aBadRate1 <= aBadRate2 )
1451           helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1452         else
1453           helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1454       }
1455       // create a new element
1456       if ( aBadRate1 <= aBadRate2 ) {
1457         newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1458         newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1459       }
1460       else {
1461         newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1462         newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1463       }
1464     } // quadratic case
1465
1466     // care of a new element
1467
1468     myLastCreatedElems.push_back(newElem1);
1469     myLastCreatedElems.push_back(newElem2);
1470     AddToSameGroups( newElem1, elem, aMesh );
1471     AddToSameGroups( newElem2, elem, aMesh );
1472
1473     // put a new triangle on the same shape
1474     if ( aShapeId )
1475       aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1476     aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1477
1478     aMesh->RemoveElement( elem );
1479   }
1480   return true;
1481 }
1482
1483 //=======================================================================
1484 /*!
1485  * \brief Split each of given quadrangles into 4 triangles.
1486  * \param theElems - The faces to be splitted. If empty all faces are split.
1487  */
1488 //=======================================================================
1489
1490 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1491 {
1492   ClearLastCreated();
1493   myLastCreatedElems.reserve( theElems.size() * 4 );
1494
1495   SMESH_MesherHelper helper( *GetMesh() );
1496   helper.SetElementsOnShape( true );
1497
1498   SMDS_ElemIteratorPtr faceIt;
1499   if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1500   else                    faceIt = SMESHUtils::elemSetIterator( theElems );
1501
1502   bool   checkUV;
1503   gp_XY  uv [9]; uv[8] = gp_XY(0,0);
1504   gp_XYZ xyz[9];
1505   vector< const SMDS_MeshNode* > nodes;
1506   SMESHDS_SubMesh*               subMeshDS = 0;
1507   TopoDS_Face                    F;
1508   Handle(Geom_Surface)           surface;
1509   TopLoc_Location                loc;
1510
1511   while ( faceIt->more() )
1512   {
1513     const SMDS_MeshElement* quad = faceIt->next();
1514     if ( !quad || quad->NbCornerNodes() != 4 )
1515       continue;
1516
1517     // get a surface the quad is on
1518
1519     if ( quad->getshapeId() < 1 )
1520     {
1521       F.Nullify();
1522       helper.SetSubShape( 0 );
1523       subMeshDS = 0;
1524     }
1525     else if ( quad->getshapeId() != helper.GetSubShapeID() )
1526     {
1527       helper.SetSubShape( quad->getshapeId() );
1528       if ( !helper.GetSubShape().IsNull() &&
1529            helper.GetSubShape().ShapeType() == TopAbs_FACE )
1530       {
1531         F = TopoDS::Face( helper.GetSubShape() );
1532         surface = BRep_Tool::Surface( F, loc );
1533         subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1534       }
1535       else
1536       {
1537         helper.SetSubShape( 0 );
1538         subMeshDS = 0;
1539       }
1540     }
1541
1542     // create a central node
1543
1544     const SMDS_MeshNode* nCentral;
1545     nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1546
1547     if ( nodes.size() == 9 )
1548     {
1549       nCentral = nodes.back();
1550     }
1551     else
1552     {
1553       size_t iN = 0;
1554       if ( F.IsNull() )
1555       {
1556         for ( ; iN < nodes.size(); ++iN )
1557           xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1558
1559         for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1560           xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1561
1562         xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1563                                    xyz[0], xyz[1], xyz[2], xyz[3],
1564                                    xyz[4], xyz[5], xyz[6], xyz[7] );
1565       }
1566       else
1567       {
1568         for ( ; iN < nodes.size(); ++iN )
1569           uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1570
1571         for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1572           uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1573
1574         uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1575                                   uv[0], uv[1], uv[2], uv[3],
1576                                   uv[4], uv[5], uv[6], uv[7] );
1577
1578         gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1579         xyz[ 8 ] = p.XYZ();
1580       }
1581
1582       nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1583                                  uv[8].X(), uv[8].Y() );
1584       myLastCreatedNodes.push_back( nCentral );
1585     }
1586
1587     // create 4 triangles
1588
1589     helper.SetIsQuadratic  ( nodes.size() > 4 );
1590     helper.SetIsBiQuadratic( nodes.size() == 9 );
1591     if ( helper.GetIsQuadratic() )
1592       helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1593
1594     GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1595
1596     for ( int i = 0; i < 4; ++i )
1597     {
1598       SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1599                                                nodes[(i+1)%4],
1600                                                nCentral );
1601       ReplaceElemInGroups( tria, quad, GetMeshDS() );
1602       myLastCreatedElems.push_back( tria );
1603     }
1604   }
1605 }
1606
1607 //=======================================================================
1608 //function : BestSplit
1609 //purpose  : Find better diagonal for cutting.
1610 //=======================================================================
1611
1612 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement*              theQuad,
1613                                  SMESH::Controls::NumericalFunctorPtr theCrit)
1614 {
1615   ClearLastCreated();
1616
1617   if (!theCrit.get())
1618     return -1;
1619
1620   if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1621     return -1;
1622
1623   if( theQuad->NbNodes()==4 ||
1624       (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1625
1626     // retrieve element nodes
1627     const SMDS_MeshNode* aNodes [4];
1628     SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1629     int i = 0;
1630     //while (itN->more())
1631     while (i<4) {
1632       aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1633     }
1634     // compare two sets of possible triangles
1635     double aBadRate1, aBadRate2; // to what extent a set is bad
1636     SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1637     SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1638     aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1639
1640     SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1641     SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1642     aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1643     // for MaxElementLength2D functor we return minimum diagonal for splitting,
1644     // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1645     if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1646       return 1; // diagonal 1-3
1647
1648     return 2; // diagonal 2-4
1649   }
1650   return -1;
1651 }
1652
1653 namespace
1654 {
1655   // Methods of splitting volumes into tetra
1656
1657   const int theHexTo5_1[5*4+1] =
1658     {
1659       0, 1, 2, 5,    0, 4, 5, 7,     0, 2, 3, 7,    2, 5, 6, 7,     0, 5, 2, 7,   -1
1660     };
1661   const int theHexTo5_2[5*4+1] =
1662     {
1663       1, 2, 3, 6,    1, 4, 5, 6,     0, 1, 3, 4,    3, 4, 6, 7,     1, 3, 4, 6,   -1
1664     };
1665   const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1666
1667   const int theHexTo6_1[6*4+1] =
1668     {
1669       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
1670     };
1671   const int theHexTo6_2[6*4+1] =
1672     {
1673       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
1674     };
1675   const int theHexTo6_3[6*4+1] =
1676     {
1677       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
1678     };
1679   const int theHexTo6_4[6*4+1] =
1680     {
1681       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
1682     };
1683   const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1684
1685   const int thePyraTo2_1[2*4+1] =
1686     {
1687       0, 1, 2, 4,    0, 2, 3, 4,   -1
1688     };
1689   const int thePyraTo2_2[2*4+1] =
1690     {
1691       1, 2, 3, 4,    1, 3, 0, 4,   -1
1692     };
1693   const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1694
1695   const int thePentaTo3_1[3*4+1] =
1696     {
1697       0, 1, 2, 3,    1, 3, 4, 2,     2, 3, 4, 5,    -1
1698     };
1699   const int thePentaTo3_2[3*4+1] =
1700     {
1701       1, 2, 0, 4,    2, 4, 5, 0,     0, 4, 5, 3,    -1
1702     };
1703   const int thePentaTo3_3[3*4+1] =
1704     {
1705       2, 0, 1, 5,    0, 5, 3, 1,     1, 5, 3, 4,    -1
1706     };
1707   const int thePentaTo3_4[3*4+1] =
1708     {
1709       0, 1, 2, 3,    1, 3, 4, 5,     2, 3, 1, 5,    -1
1710     };
1711   const int thePentaTo3_5[3*4+1] =
1712     {
1713       1, 2, 0, 4,    2, 4, 5, 3,     0, 4, 2, 3,    -1
1714     };
1715   const int thePentaTo3_6[3*4+1] =
1716     {
1717       2, 0, 1, 5,    0, 5, 3, 4,     1, 5, 0, 4,    -1
1718     };
1719   const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1720                                 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1721
1722   // Methods of splitting hexahedron into prisms
1723
1724   const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1725     {
1726       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
1727     };
1728   const int theHexTo4Prisms_LR[6*4+1] = // left-right
1729     {
1730       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
1731     };
1732   const int theHexTo4Prisms_FB[6*4+1] = // front-back
1733     {
1734       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
1735     };
1736
1737   const int theHexTo2Prisms_BT_1[6*2+1] =
1738     {
1739       0, 1, 3, 4, 5, 7,    1, 2, 3, 5, 6, 7,   -1
1740     };
1741   const int theHexTo2Prisms_BT_2[6*2+1] =
1742     {
1743       0, 1, 2, 4, 5, 6,    0, 2, 3, 4, 6, 7,   -1
1744     };
1745   const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1746
1747   const int theHexTo2Prisms_LR_1[6*2+1] =
1748     {
1749       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1750     };
1751   const int theHexTo2Prisms_LR_2[6*2+1] =
1752     {
1753       1, 0, 4, 2, 3, 7,    1, 4, 5, 2, 7, 6,   -1
1754     };
1755   const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1756
1757   const int theHexTo2Prisms_FB_1[6*2+1] =
1758     {
1759       0, 3, 4, 1, 2, 5,    3, 7, 4, 2, 6, 5,   -1
1760     };
1761   const int theHexTo2Prisms_FB_2[6*2+1] =
1762     {
1763       0, 3, 7, 1, 2, 7,    0, 7, 4, 1, 6, 5,   -1
1764     };
1765   const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1766
1767
1768   struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1769   {
1770     int _n1, _n2, _n3;
1771     TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1772     bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1773     bool hasAdjacentVol( const SMDS_MeshElement*    elem,
1774                          const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1775   };
1776   struct TSplitMethod
1777   {
1778     int        _nbSplits;
1779     int        _nbCorners;
1780     const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1781     bool       _baryNode;     //!< additional node is to be created at cell barycenter
1782     bool       _ownConn;      //!< to delete _connectivity in destructor
1783     map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1784
1785     TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1786       : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1787     ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1788     bool hasFacet( const TTriangleFacet& facet ) const
1789     {
1790       if ( _nbCorners == 4 )
1791       {
1792         const int* tetConn = _connectivity;
1793         for ( ; tetConn[0] >= 0; tetConn += 4 )
1794           if (( facet.contains( tetConn[0] ) +
1795                 facet.contains( tetConn[1] ) +
1796                 facet.contains( tetConn[2] ) +
1797                 facet.contains( tetConn[3] )) == 3 )
1798             return true;
1799       }
1800       else // prism, _nbCorners == 6
1801       {
1802         const int* prismConn = _connectivity;
1803         for ( ; prismConn[0] >= 0; prismConn += 6 )
1804         {
1805           if (( facet.contains( prismConn[0] ) &&
1806                 facet.contains( prismConn[1] ) &&
1807                 facet.contains( prismConn[2] ))
1808               ||
1809               ( facet.contains( prismConn[3] ) &&
1810                 facet.contains( prismConn[4] ) &&
1811                 facet.contains( prismConn[5] )))
1812             return true;
1813         }
1814       }
1815       return false;
1816     }
1817   };
1818
1819   //=======================================================================
1820   /*!
1821    * \brief return TSplitMethod for the given element to split into tetrahedra
1822    */
1823   //=======================================================================
1824
1825   TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1826   {
1827     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1828
1829     // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1830     // an edge and a face barycenter; tertaherdons are based on triangles and
1831     // a volume barycenter
1832     const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1833
1834     // Find out how adjacent volumes are split
1835
1836     vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1837     int hasAdjacentSplits = 0, maxTetConnSize = 0;
1838     for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1839     {
1840       int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1841       maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1842       if ( nbNodes < 4 ) continue;
1843
1844       list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1845       const int* nInd = vol.GetFaceNodesIndices( iF );
1846       if ( nbNodes == 4 )
1847       {
1848         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1849         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1850         if      ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1851         else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1852       }
1853       else
1854       {
1855         int iCom = 0; // common node of triangle faces to split into
1856         for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1857         {
1858           TTriangleFacet t012( nInd[ iQ * ( iCom             )],
1859                                nInd[ iQ * ( (iCom+1)%nbNodes )],
1860                                nInd[ iQ * ( (iCom+2)%nbNodes )]);
1861           TTriangleFacet t023( nInd[ iQ * ( iCom             )],
1862                                nInd[ iQ * ( (iCom+2)%nbNodes )],
1863                                nInd[ iQ * ( (iCom+3)%nbNodes )]);
1864           if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1865           {
1866             triaSplits.push_back( t012 );
1867             triaSplits.push_back( t023 );
1868             break;
1869           }
1870         }
1871       }
1872       if ( !triaSplits.empty() )
1873         hasAdjacentSplits = true;
1874     }
1875
1876     // Among variants of split method select one compliant with adjacent volumes
1877
1878     TSplitMethod method;
1879     if ( !vol.Element()->IsPoly() && !is24TetMode )
1880     {
1881       int nbVariants = 2, nbTet = 0;
1882       const int** connVariants = 0;
1883       switch ( vol.Element()->GetEntityType() )
1884       {
1885       case SMDSEntity_Hexa:
1886       case SMDSEntity_Quad_Hexa:
1887       case SMDSEntity_TriQuad_Hexa:
1888         if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1889           connVariants = theHexTo5, nbTet = 5;
1890         else
1891           connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1892         break;
1893       case SMDSEntity_Pyramid:
1894       case SMDSEntity_Quad_Pyramid:
1895         connVariants = thePyraTo2;  nbTet = 2;
1896         break;
1897       case SMDSEntity_Penta:
1898       case SMDSEntity_Quad_Penta:
1899         connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1900         break;
1901       default:
1902         nbVariants = 0;
1903       }
1904       for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1905       {
1906         // check method compliancy with adjacent tetras,
1907         // all found splits must be among facets of tetras described by this method
1908         method = TSplitMethod( nbTet, connVariants[variant] );
1909         if ( hasAdjacentSplits && method._nbSplits > 0 )
1910         {
1911           bool facetCreated = true;
1912           for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1913           {
1914             list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1915             for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1916               facetCreated = method.hasFacet( *facet );
1917           }
1918           if ( !facetCreated )
1919             method = TSplitMethod(0); // incompatible method
1920         }
1921       }
1922     }
1923     if ( method._nbSplits < 1 )
1924     {
1925       // No standard method is applicable, use a generic solution:
1926       // each facet of a volume is split into triangles and
1927       // each of triangles and a volume barycenter form a tetrahedron.
1928
1929       const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1930
1931       int* connectivity = new int[ maxTetConnSize + 1 ];
1932       method._connectivity = connectivity;
1933       method._ownConn = true;
1934       method._baryNode = !isHex27; // to create central node or not
1935
1936       int connSize = 0;
1937       int baryCenInd = vol.NbNodes() - int( isHex27 );
1938       for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1939       {
1940         const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1941         const int*   nInd = vol.GetFaceNodesIndices( iF );
1942         // find common node of triangle facets of tetra to create
1943         int iCommon = 0; // index in linear numeration
1944         const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1945         if ( !triaSplits.empty() )
1946         {
1947           // by found facets
1948           const TTriangleFacet* facet = &triaSplits.front();
1949           for ( ; iCommon < nbNodes-1 ; ++iCommon )
1950             if ( facet->contains( nInd[ iQ * iCommon ]) &&
1951                  facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1952               break;
1953         }
1954         else if ( nbNodes > 3 && !is24TetMode )
1955         {
1956           // find the best method of splitting into triangles by aspect ratio
1957           SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1958           map< double, int > badness2iCommon;
1959           const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1960           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1961           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1962           {
1963             double badness = 0;
1964             for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1965             {
1966               SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon         )],
1967                                       nodes[ iQ*((iLast-1)%nbNodes)],
1968                                       nodes[ iQ*((iLast  )%nbNodes)]);
1969               badness += getBadRate( &tria, aspectRatio );
1970             }
1971             badness2iCommon.insert( make_pair( badness, iCommon ));
1972           }
1973           // use iCommon with lowest badness
1974           iCommon = badness2iCommon.begin()->second;
1975         }
1976         if ( iCommon >= nbNodes )
1977           iCommon = 0; // something wrong
1978
1979         // fill connectivity of tetrahedra based on a current face
1980         int nbTet = nbNodes - 2;
1981         if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1982         {
1983           int faceBaryCenInd;
1984           if ( isHex27 )
1985           {
1986             faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1987             method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1988           }
1989           else
1990           {
1991             method._faceBaryNode[ iF ] = 0;
1992             faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1993           }
1994           nbTet = nbNodes;
1995           for ( int i = 0; i < nbTet; ++i )
1996           {
1997             int i1 = i, i2 = (i+1) % nbNodes;
1998             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1999             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2000             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2001             connectivity[ connSize++ ] = faceBaryCenInd;
2002             connectivity[ connSize++ ] = baryCenInd;
2003           }
2004         }
2005         else
2006         {
2007           for ( int i = 0; i < nbTet; ++i )
2008           {
2009             int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2010             if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2011             connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2012             connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2013             connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2014             connectivity[ connSize++ ] = baryCenInd;
2015           }
2016         }
2017         method._nbSplits += nbTet;
2018
2019       } // loop on volume faces
2020
2021       connectivity[ connSize++ ] = -1;
2022
2023     } // end of generic solution
2024
2025     return method;
2026   }
2027   //=======================================================================
2028   /*!
2029    * \brief return TSplitMethod to split haxhedron into prisms
2030    */
2031   //=======================================================================
2032
2033   TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2034                                     const int        methodFlags,
2035                                     const int        facetToSplit)
2036   {
2037     // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2038     // B, T, L, B, R, F
2039     const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2040
2041     if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2042     {
2043       static TSplitMethod to4methods[4]; // order BT, LR, FB
2044       if ( to4methods[iF]._nbSplits == 0 )
2045       {
2046         switch ( iF ) {
2047         case 0:
2048           to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2049           to4methods[iF]._faceBaryNode[ 0 ] = 0;
2050           to4methods[iF]._faceBaryNode[ 1 ] = 0;
2051           break;
2052         case 1:
2053           to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2054           to4methods[iF]._faceBaryNode[ 2 ] = 0;
2055           to4methods[iF]._faceBaryNode[ 4 ] = 0;
2056           break;
2057         case 2:
2058           to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2059           to4methods[iF]._faceBaryNode[ 3 ] = 0;
2060           to4methods[iF]._faceBaryNode[ 5 ] = 0;
2061           break;
2062         default: return to4methods[3];
2063         }
2064         to4methods[iF]._nbSplits  = 4;
2065         to4methods[iF]._nbCorners = 6;
2066       }
2067       return to4methods[iF];
2068     }
2069     // else if ( methodFlags == HEXA_TO_2_PRISMS )
2070
2071     TSplitMethod method;
2072
2073     const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2074
2075     const int nbVariants = 2, nbSplits = 2;
2076     const int** connVariants = 0;
2077     switch ( iF ) {
2078     case 0: connVariants = theHexTo2Prisms_BT; break;
2079     case 1: connVariants = theHexTo2Prisms_LR; break;
2080     case 2: connVariants = theHexTo2Prisms_FB; break;
2081     default: return method;
2082     }
2083
2084     // look for prisms adjacent via facetToSplit and an opposite one
2085     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2086     {
2087       int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2088       int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2089       if ( nbNodes != 4 ) return method;
2090
2091       const int* nInd = vol.GetFaceNodesIndices( iFacet );
2092       TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2093       TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2094       TTriangleFacet* t;
2095       if      ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2096         t = &t012;
2097       else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2098         t = &t123;
2099       else
2100         continue;
2101
2102       // there are adjacent prism
2103       for ( int variant = 0; variant < nbVariants; ++variant )
2104       {
2105         // check method compliancy with adjacent prisms,
2106         // the found prism facets must be among facets of prisms described by current method
2107         method._nbSplits     = nbSplits;
2108         method._nbCorners    = 6;
2109         method._connectivity = connVariants[ variant ];
2110         if ( method.hasFacet( *t ))
2111           return method;
2112       }
2113     }
2114
2115     // No adjacent prisms. Select a variant with a best aspect ratio.
2116
2117     double badness[2] = { 0., 0. };
2118     static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2119     const SMDS_MeshNode** nodes = vol.GetNodes();
2120     for ( int variant = 0; variant < nbVariants; ++variant )
2121       for ( int is2nd = 0; is2nd < 2; ++is2nd )
2122       {
2123         int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2124         const int*             nInd = vol.GetFaceNodesIndices( iFacet );
2125
2126         method._connectivity = connVariants[ variant ];
2127         TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2128         TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2129         TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2130
2131         SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2132                                 nodes[ t->_n2 ],
2133                                 nodes[ t->_n3 ] );
2134         badness[ variant ] += getBadRate( &tria, aspectRatio );
2135       }
2136     const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2137
2138     method._nbSplits     = nbSplits;
2139     method._nbCorners    = 6;
2140     method._connectivity = connVariants[ iBetter ];
2141
2142     return method;
2143   }
2144
2145   //================================================================================
2146   /*!
2147    * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2148    */
2149   //================================================================================
2150
2151   bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement*    elem,
2152                                        const SMDSAbs_GeometryType geom ) const
2153   {
2154     // find the tetrahedron including the three nodes of facet
2155     const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2156     const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2157     const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2158     SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2159     while ( volIt1->more() )
2160     {
2161       const SMDS_MeshElement* v = volIt1->next();
2162       if ( v->GetGeomType() != geom )
2163         continue;
2164       const int lastCornerInd = v->NbCornerNodes() - 1;
2165       if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2166         continue; // medium node not allowed
2167       const int ind2 = v->GetNodeIndex( n2 );
2168       if ( ind2 < 0 || lastCornerInd < ind2 )
2169         continue;
2170       const int ind3 = v->GetNodeIndex( n3 );
2171       if ( ind3 < 0 || lastCornerInd < ind3 )
2172         continue;
2173       return true;
2174     }
2175     return false;
2176   }
2177
2178   //=======================================================================
2179   /*!
2180    * \brief A key of a face of volume
2181    */
2182   //=======================================================================
2183
2184   struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2185   {
2186     TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2187     {
2188       TIDSortedNodeSet sortedNodes;
2189       const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2190       int nbNodes = vol.NbFaceNodes( iF );
2191       const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2192       for ( int i = 0; i < nbNodes; i += iQ )
2193         sortedNodes.insert( fNodes[i] );
2194       TIDSortedNodeSet::iterator n = sortedNodes.begin();
2195       first.first   = (*(n++))->GetID();
2196       first.second  = (*(n++))->GetID();
2197       second.first  = (*(n++))->GetID();
2198       second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2199     }
2200   };
2201 } // namespace
2202
2203 //=======================================================================
2204 //function : SplitVolumes
2205 //purpose  : Split volume elements into tetrahedra or prisms.
2206 //           If facet ID < 0, element is split into tetrahedra,
2207 //           else a hexahedron is split into prisms so that the given facet is
2208 //           split into triangles
2209 //=======================================================================
2210
2211 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2212                                      const int            theMethodFlags)
2213 {
2214   SMDS_VolumeTool    volTool;
2215   SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2216   fHelper.ToFixNodeParameters( true );
2217
2218   SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2219   SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2220
2221   SMESH_SequenceOfElemPtr newNodes, newElems;
2222
2223   // map face of volume to it's baricenrtic node
2224   map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2225   double bc[3];
2226   vector<const SMDS_MeshElement* > splitVols;
2227
2228   TFacetOfElem::const_iterator elem2facet = theElems.begin();
2229   for ( ; elem2facet != theElems.end(); ++elem2facet )
2230   {
2231     const SMDS_MeshElement* elem = elem2facet->first;
2232     const int       facetToSplit = elem2facet->second;
2233     if ( elem->GetType() != SMDSAbs_Volume )
2234       continue;
2235     const SMDSAbs_EntityType geomType = elem->GetEntityType();
2236     if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2237       continue;
2238
2239     if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2240
2241     TSplitMethod splitMethod = ( facetToSplit < 0  ?
2242                                  getTetraSplitMethod( volTool, theMethodFlags ) :
2243                                  getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2244     if ( splitMethod._nbSplits < 1 ) continue;
2245
2246     // find submesh to add new tetras to
2247     if ( !subMesh || !subMesh->Contains( elem ))
2248     {
2249       int shapeID = FindShape( elem );
2250       helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2251       subMesh = GetMeshDS()->MeshElements( shapeID );
2252     }
2253     int iQ;
2254     if ( elem->IsQuadratic() )
2255     {
2256       iQ = 2;
2257       // add quadratic links to the helper
2258       for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2259       {
2260         const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2261         int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2262         for ( int iN = 0; iN < nbN; iN += iQ )
2263           helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2264       }
2265       helper.SetIsQuadratic( true );
2266     }
2267     else
2268     {
2269       iQ = 1;
2270       helper.SetIsQuadratic( false );
2271     }
2272     vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2273                                         volTool.GetNodes() + elem->NbNodes() );
2274     helper.SetElementsOnShape( true );
2275     if ( splitMethod._baryNode )
2276     {
2277       // make a node at barycenter
2278       volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2279       SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2280       nodes.push_back( gcNode );
2281       newNodes.push_back( gcNode );
2282     }
2283     if ( !splitMethod._faceBaryNode.empty() )
2284     {
2285       // make or find baricentric nodes of faces
2286       map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2287       for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2288       {
2289         map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2290           volFace2BaryNode.insert
2291           ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2292         if ( !f_n->second )
2293         {
2294           volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2295           newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2296         }
2297         nodes.push_back( iF_n->second = f_n->second );
2298       }
2299     }
2300
2301     // make new volumes
2302     splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2303     const int* volConn = splitMethod._connectivity;
2304     if ( splitMethod._nbCorners == 4 ) // tetra
2305       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2306         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2307                                                                nodes[ volConn[1] ],
2308                                                                nodes[ volConn[2] ],
2309                                                                nodes[ volConn[3] ]));
2310     else // prisms
2311       for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2312         newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2313                                                                nodes[ volConn[1] ],
2314                                                                nodes[ volConn[2] ],
2315                                                                nodes[ volConn[3] ],
2316                                                                nodes[ volConn[4] ],
2317                                                                nodes[ volConn[5] ]));
2318
2319     ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2320
2321     // Split faces on sides of the split volume
2322
2323     const SMDS_MeshNode** volNodes = volTool.GetNodes();
2324     for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2325     {
2326       const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2327       if ( nbNodes < 4 ) continue;
2328
2329       // find an existing face
2330       vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2331                                            volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2332       while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2333                                                                        /*noMedium=*/false))
2334       {
2335         // make triangles
2336         helper.SetElementsOnShape( false );
2337         vector< const SMDS_MeshElement* > triangles;
2338
2339         // find submesh to add new triangles in
2340         if ( !fSubMesh || !fSubMesh->Contains( face ))
2341         {
2342           int shapeID = FindShape( face );
2343           fSubMesh = GetMeshDS()->MeshElements( shapeID );
2344         }
2345         map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2346         if ( iF_n != splitMethod._faceBaryNode.end() )
2347         {
2348           const SMDS_MeshNode *baryNode = iF_n->second;
2349           for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2350           {
2351             const SMDS_MeshNode* n1 = fNodes[iN];
2352             const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2353             const SMDS_MeshNode *n3 = baryNode;
2354             if ( !volTool.IsFaceExternal( iF ))
2355               swap( n2, n3 );
2356             triangles.push_back( helper.AddFace( n1,n2,n3 ));
2357           }
2358           if ( fSubMesh ) // update position of the bary node on geometry
2359           {
2360             if ( subMesh )
2361               subMesh->RemoveNode( baryNode, false );
2362             GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2363             const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2364             if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2365             {
2366               fHelper.SetSubShape( s );
2367               gp_XY uv( 1e100, 1e100 );
2368               double distXYZ[4];
2369               if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2370                                          uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2371                    uv.X() < 1e100 )
2372               {
2373                 // node is too far from the surface
2374                 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2375                 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2376                   ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2377               }
2378             }
2379           }
2380         }
2381         else
2382         {
2383           // among possible triangles create ones discribed by split method
2384           const int* nInd = volTool.GetFaceNodesIndices( iF );
2385           int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2386           int iCom = 0; // common node of triangle faces to split into
2387           list< TTriangleFacet > facets;
2388           for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2389           {
2390             TTriangleFacet t012( nInd[ iQ * ( iCom                )],
2391                                  nInd[ iQ * ( (iCom+1)%nbNodes )],
2392                                  nInd[ iQ * ( (iCom+2)%nbNodes )]);
2393             TTriangleFacet t023( nInd[ iQ * ( iCom                )],
2394                                  nInd[ iQ * ( (iCom+2)%nbNodes )],
2395                                  nInd[ iQ * ( (iCom+3)%nbNodes )]);
2396             if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2397             {
2398               facets.push_back( t012 );
2399               facets.push_back( t023 );
2400               for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2401                 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom             )],
2402                                                   nInd[ iQ * ((iLast-1)%nbNodes )],
2403                                                   nInd[ iQ * ((iLast  )%nbNodes )]));
2404               break;
2405             }
2406           }
2407           list< TTriangleFacet >::iterator facet = facets.begin();
2408           if ( facet == facets.end() )
2409             break;
2410           for ( ; facet != facets.end(); ++facet )
2411           {
2412             if ( !volTool.IsFaceExternal( iF ))
2413               swap( facet->_n2, facet->_n3 );
2414             triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2415                                                  volNodes[ facet->_n2 ],
2416                                                  volNodes[ facet->_n3 ]));
2417           }
2418         }
2419         for ( size_t i = 0; i < triangles.size(); ++i )
2420         {
2421           if ( !triangles[ i ]) continue;
2422           if ( fSubMesh )
2423             fSubMesh->AddElement( triangles[ i ]);
2424           newElems.push_back( triangles[ i ]);
2425         }
2426         ReplaceElemInGroups( face, triangles, GetMeshDS() );
2427         GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2428
2429       } // while a face based on facet nodes exists
2430     } // loop on volume faces to split them into triangles
2431
2432     GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2433
2434     if ( geomType == SMDSEntity_TriQuad_Hexa )
2435     {
2436       // remove medium nodes that could become free
2437       for ( int i = 20; i < volTool.NbNodes(); ++i )
2438         if ( volNodes[i]->NbInverseElements() == 0 )
2439           GetMeshDS()->RemoveNode( volNodes[i] );
2440     }
2441   } // loop on volumes to split
2442
2443   myLastCreatedNodes = newNodes;
2444   myLastCreatedElems = newElems;
2445 }
2446
2447 //=======================================================================
2448 //function : GetHexaFacetsToSplit
2449 //purpose  : For hexahedra that will be split into prisms, finds facets to
2450 //           split into triangles. Only hexahedra adjacent to the one closest
2451 //           to theFacetNormal.Location() are returned.
2452 //param [in,out] theHexas - the hexahedra
2453 //param [in]     theFacetNormal - facet normal
2454 //param [out]    theFacets - the hexahedra and found facet IDs
2455 //=======================================================================
2456
2457 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2458                                              const gp_Ax1&     theFacetNormal,
2459                                              TFacetOfElem &    theFacets)
2460 {
2461 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2462
2463   // Find a hexa closest to the location of theFacetNormal
2464
2465   const SMDS_MeshElement* startHex;
2466   {
2467     // get SMDS_ElemIteratorPtr on theHexas
2468     typedef const SMDS_MeshElement*                                      TValue;
2469     typedef TIDSortedElemSet::iterator                                   TSetIterator;
2470     typedef SMDS::SimpleAccessor<TValue,TSetIterator>                    TAccesor;
2471     typedef SMDS_MeshElement::GeomFilter                                 TFilter;
2472     typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2473     SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2474       ( new TElemSetIter( theHexas.begin(),
2475                           theHexas.end(),
2476                           SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2477
2478     SMESH_ElementSearcher* searcher =
2479       SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2480
2481     startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2482
2483     delete searcher;
2484
2485     if ( !startHex )
2486       throw SALOME_Exception( THIS_METHOD "startHex not found");
2487   }
2488
2489   // Select a facet of startHex by theFacetNormal
2490
2491   SMDS_VolumeTool vTool( startHex );
2492   double norm[3], dot, maxDot = 0;
2493   int facetID = -1;
2494   for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2495     if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2496     {
2497       dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2498       if ( dot > maxDot )
2499       {
2500         facetID = iF;
2501         maxDot = dot;
2502       }
2503     }
2504   if ( facetID < 0 )
2505     throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2506
2507   // Fill theFacets starting from facetID of startHex
2508
2509   // facets used for seach of volumes adjacent to already treated ones
2510   typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2511   typedef map< TVolumeFaceKey, TElemFacets  > TFacetMap;
2512   TFacetMap facetsToCheck;
2513
2514   set<const SMDS_MeshNode*> facetNodes;
2515   const SMDS_MeshElement*   curHex;
2516
2517   const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2518
2519   while ( startHex )
2520   {
2521     // move in two directions from startHex via facetID
2522     for ( int is2nd = 0; is2nd < 2; ++is2nd )
2523     {
2524       curHex       = startHex;
2525       int curFacet = facetID;
2526       if ( is2nd ) // do not treat startHex twice
2527       {
2528         vTool.Set( curHex );
2529         if ( vTool.IsFreeFace( curFacet, &curHex ))
2530         {
2531           curHex = 0;
2532         }
2533         else
2534         {
2535           vTool.GetFaceNodes( curFacet, facetNodes );
2536           vTool.Set( curHex );
2537           curFacet = vTool.GetFaceIndex( facetNodes );
2538         }
2539       }
2540       while ( curHex )
2541       {
2542         // store a facet to split
2543         if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2544         {
2545           theFacets.insert( make_pair( curHex, -1 ));
2546           break;
2547         }
2548         if ( !allHex && !theHexas.count( curHex ))
2549           break;
2550
2551         pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2552           theFacets.insert( make_pair( curHex, curFacet ));
2553         if ( !facetIt2isNew.second )
2554           break;
2555
2556         // remember not-to-split facets in facetsToCheck
2557         int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2558         for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2559         {
2560           if ( iF == curFacet && iF == oppFacet )
2561             continue;
2562           TVolumeFaceKey facetKey ( vTool, iF );
2563           TElemFacets    elemFacet( facetIt2isNew.first, iF );
2564           pair< TFacetMap::iterator, bool > it2isnew =
2565             facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2566           if ( !it2isnew.second )
2567             facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2568         }
2569         // pass to a volume adjacent via oppFacet
2570         if ( vTool.IsFreeFace( oppFacet, &curHex ))
2571         {
2572           curHex = 0;
2573         }
2574         else
2575         {
2576           // get a new curFacet
2577           vTool.GetFaceNodes( oppFacet, facetNodes );
2578           vTool.Set( curHex );
2579           curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2580         }
2581       }
2582     } // move in two directions from startHex via facetID
2583
2584     // Find a new startHex by facetsToCheck
2585
2586     startHex = 0;
2587     facetID  = -1;
2588     TFacetMap::iterator fIt = facetsToCheck.begin();
2589     while ( !startHex && fIt != facetsToCheck.end() )
2590     {
2591       const TElemFacets&  elemFacets = fIt->second;
2592       const SMDS_MeshElement*    hex = elemFacets.first->first;
2593       int                 splitFacet = elemFacets.first->second;
2594       int               lateralFacet = elemFacets.second;
2595       facetsToCheck.erase( fIt );
2596       fIt = facetsToCheck.begin();
2597
2598       vTool.Set( hex );
2599       if ( vTool.IsFreeFace( lateralFacet, &curHex ) || 
2600            curHex->GetGeomType() != SMDSGeom_HEXA )
2601         continue;
2602       if ( !allHex && !theHexas.count( curHex ))
2603         continue;
2604
2605       startHex = curHex;
2606
2607       // find a facet of startHex to split
2608
2609       set<const SMDS_MeshNode*> lateralNodes;
2610       vTool.GetFaceNodes( lateralFacet, lateralNodes );
2611       vTool.GetFaceNodes( splitFacet,   facetNodes );
2612       int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2613       vTool.Set( startHex );
2614       lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2615
2616       // look for a facet of startHex having common nodes with facetNodes
2617       // but not lateralFacet
2618       for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2619       {
2620         if ( iF == lateralFacet )
2621           continue;
2622         int nbCommonNodes = 0;
2623         const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2624         for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2625           nbCommonNodes += facetNodes.count( nn[ iN ]);
2626
2627         if ( nbCommonNodes >= 2 )
2628         {
2629           facetID = iF;
2630           break;
2631         }
2632       }
2633       if ( facetID < 0 )
2634         throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2635     }
2636   } //   while ( startHex )
2637
2638   return;
2639 }
2640
2641 namespace
2642 {
2643   //================================================================================
2644   /*!
2645    * \brief Selects nodes of several elements according to a given interlace
2646    *  \param [in] srcNodes - nodes to select from
2647    *  \param [out] tgtNodesVec - array of nodes of several elements to fill in
2648    *  \param [in] interlace - indices of nodes for all elements
2649    *  \param [in] nbElems - nb of elements
2650    *  \param [in] nbNodes - nb of nodes in each element
2651    *  \param [in] mesh - the mesh
2652    *  \param [out] elemQueue - a list to push elements found by the selected nodes
2653    *  \param [in] type - type of elements to look for
2654    */
2655   //================================================================================
2656
2657   void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2658                     vector< const SMDS_MeshNode* >*       tgtNodesVec,
2659                     const int*                            interlace,
2660                     const int                             nbElems,
2661                     const int                             nbNodes,
2662                     SMESHDS_Mesh*                         mesh = 0,
2663                     list< const SMDS_MeshElement* >*      elemQueue=0,
2664                     SMDSAbs_ElementType                   type=SMDSAbs_All)
2665   {
2666     for ( int iE = 0; iE < nbElems; ++iE )
2667     {
2668       vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2669       const int*                         select = & interlace[iE*nbNodes];
2670       elemNodes.resize( nbNodes );
2671       for ( int iN = 0; iN < nbNodes; ++iN )
2672         elemNodes[iN] = srcNodes[ select[ iN ]];
2673     }
2674     const SMDS_MeshElement* e;
2675     if ( elemQueue )
2676       for ( int iE = 0; iE < nbElems; ++iE )
2677         if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2678           elemQueue->push_back( e );
2679   }
2680 }
2681
2682 //=======================================================================
2683 /*
2684  * Split bi-quadratic elements into linear ones without creation of additional nodes
2685  *   - bi-quadratic triangle will be split into 3 linear quadrangles;
2686  *   - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2687  *   - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2688  *   Quadratic elements of lower dimension  adjacent to the split bi-quadratic element
2689  *   will be split in order to keep the mesh conformal.
2690  *  \param elems - elements to split
2691  */
2692 //=======================================================================
2693
2694 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2695 {
2696   vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2697   vector<const SMDS_MeshElement* > splitElems;
2698   list< const SMDS_MeshElement* > elemQueue;
2699   list< const SMDS_MeshElement* >::iterator elemIt;
2700
2701   SMESHDS_Mesh * mesh = GetMeshDS();
2702   ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2703   int nbElems, nbNodes;
2704
2705   TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2706   for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2707   {
2708     elemQueue.clear();
2709     elemQueue.push_back( *elemSetIt );
2710     for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2711     {
2712       const SMDS_MeshElement* elem = *elemIt;
2713       switch( elem->GetEntityType() )
2714       {
2715       case SMDSEntity_TriQuad_Hexa: // HEX27
2716       {
2717         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2718         nbElems  = nbNodes = 8;
2719         elemType = & hexaType;
2720
2721         // get nodes for new elements
2722         static int vInd[8][8] = {{ 0,8,20,11,   16,21,26,24 },
2723                                  { 1,9,20,8,    17,22,26,21 },
2724                                  { 2,10,20,9,   18,23,26,22 },
2725                                  { 3,11,20,10,  19,24,26,23 },
2726                                  { 16,21,26,24, 4,12,25,15  },
2727                                  { 17,22,26,21, 5,13,25,12  },
2728                                  { 18,23,26,22, 6,14,25,13  },
2729                                  { 19,24,26,23, 7,15,25,14  }};
2730         selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2731
2732         // add boundary faces to elemQueue
2733         static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11,   20 },
2734                                  { 4,5,6,7, 12,13,14,15, 25 },
2735                                  { 0,1,5,4, 8,17,12,16,  21 },
2736                                  { 1,2,6,5, 9,18,13,17,  22 },
2737                                  { 2,3,7,6, 10,19,14,18, 23 },
2738                                  { 3,0,4,7, 11,16,15,19, 24 }};
2739         selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2740
2741         // add boundary segments to elemQueue
2742         static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2743                                   { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2744                                   { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2745         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2746         break;
2747       }
2748       case SMDSEntity_BiQuad_Triangle: // TRIA7
2749       {
2750         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2751         nbElems = 3;
2752         nbNodes = 4;
2753         elemType = & quadType;
2754
2755         // get nodes for new elements
2756         static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2757         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2758
2759         // add boundary segments to elemQueue
2760         static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2761         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2762         break;
2763       }
2764       case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2765       {
2766         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2767         nbElems = 4;
2768         nbNodes = 4;
2769         elemType = & quadType;
2770
2771         // get nodes for new elements
2772         static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2773         selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2774
2775         // add boundary segments to elemQueue
2776         static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2777         selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2778         break;
2779       }
2780       case SMDSEntity_Quad_Edge:
2781       {
2782         if ( elemIt == elemQueue.begin() )
2783           continue; // an elem is in theElems
2784         elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785         nbElems = 2;
2786         nbNodes = 2;
2787         elemType = & segType;
2788
2789         // get nodes for new elements
2790         static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2791         selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2792         break;
2793       }
2794       default: continue;
2795       } // switch( elem->GetEntityType() )
2796
2797       // Create new elements
2798
2799       SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2800
2801       splitElems.clear();
2802
2803       //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2804       mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2805       //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2806       //elemType->SetID( -1 );
2807
2808       for ( int iE = 0; iE < nbElems; ++iE )
2809         splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2810
2811
2812       ReplaceElemInGroups( elem, splitElems, mesh );
2813
2814       if ( subMesh )
2815         for ( size_t i = 0; i < splitElems.size(); ++i )
2816           subMesh->AddElement( splitElems[i] );
2817     }
2818   }
2819 }
2820
2821 //=======================================================================
2822 //function : AddToSameGroups
2823 //purpose  : add elemToAdd to the groups the elemInGroups belongs to
2824 //=======================================================================
2825
2826 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2827                                         const SMDS_MeshElement* elemInGroups,
2828                                         SMESHDS_Mesh *          aMesh)
2829 {
2830   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2831   if (!groups.empty()) {
2832     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2833     for ( ; grIt != groups.end(); grIt++ ) {
2834       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2835       if ( group && group->Contains( elemInGroups ))
2836         group->SMDSGroup().Add( elemToAdd );
2837     }
2838   }
2839 }
2840
2841
2842 //=======================================================================
2843 //function : RemoveElemFromGroups
2844 //purpose  : Remove removeelem to the groups the elemInGroups belongs to
2845 //=======================================================================
2846 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2847                                              SMESHDS_Mesh *          aMesh)
2848 {
2849   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2850   if (!groups.empty())
2851   {
2852     set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2853     for (; GrIt != groups.end(); GrIt++)
2854     {
2855       SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2856       if (!grp || grp->IsEmpty()) continue;
2857       grp->SMDSGroup().Remove(removeelem);
2858     }
2859   }
2860 }
2861
2862 //================================================================================
2863 /*!
2864  * \brief Replace elemToRm by elemToAdd in the all groups
2865  */
2866 //================================================================================
2867
2868 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2869                                             const SMDS_MeshElement* elemToAdd,
2870                                             SMESHDS_Mesh *          aMesh)
2871 {
2872   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2873   if (!groups.empty()) {
2874     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2875     for ( ; grIt != groups.end(); grIt++ ) {
2876       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2877       if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2878         group->SMDSGroup().Add( elemToAdd );
2879     }
2880   }
2881 }
2882
2883 //================================================================================
2884 /*!
2885  * \brief Replace elemToRm by elemToAdd in the all groups
2886  */
2887 //================================================================================
2888
2889 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement*                elemToRm,
2890                                             const vector<const SMDS_MeshElement*>& elemToAdd,
2891                                             SMESHDS_Mesh *                         aMesh)
2892 {
2893   const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2894   if (!groups.empty())
2895   {
2896     set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2897     for ( ; grIt != groups.end(); grIt++ ) {
2898       SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2899       if ( group && group->SMDSGroup().Remove( elemToRm ) )
2900         for ( size_t i = 0; i < elemToAdd.size(); ++i )
2901           group->SMDSGroup().Add( elemToAdd[ i ] );
2902     }
2903   }
2904 }
2905
2906 //=======================================================================
2907 //function : QuadToTri
2908 //purpose  : Cut quadrangles into triangles.
2909 //           theCrit is used to select a diagonal to cut
2910 //=======================================================================
2911
2912 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2913                                   const bool         the13Diag)
2914 {
2915   ClearLastCreated();
2916   myLastCreatedElems.reserve( theElems.size() * 2 );
2917
2918   SMESHDS_Mesh *       aMesh = GetMeshDS();
2919   Handle(Geom_Surface) surface;
2920   SMESH_MesherHelper   helper( *GetMesh() );
2921
2922   TIDSortedElemSet::iterator itElem;
2923   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2924   {
2925     const SMDS_MeshElement* elem = *itElem;
2926     if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2927       continue;
2928
2929     if ( elem->NbNodes() == 4 ) {
2930       // retrieve element nodes
2931       const SMDS_MeshNode* aNodes [4];
2932       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2933       int i = 0;
2934       while ( itN->more() )
2935         aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2936
2937       int aShapeId = FindShape( elem );
2938       const SMDS_MeshElement* newElem1 = 0;
2939       const SMDS_MeshElement* newElem2 = 0;
2940       if ( the13Diag ) {
2941         newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2942         newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2943       }
2944       else {
2945         newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2946         newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2947       }
2948       myLastCreatedElems.push_back(newElem1);
2949       myLastCreatedElems.push_back(newElem2);
2950       // put a new triangle on the same shape and add to the same groups
2951       if ( aShapeId )
2952       {
2953         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2954         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2955       }
2956       AddToSameGroups( newElem1, elem, aMesh );
2957       AddToSameGroups( newElem2, elem, aMesh );
2958       aMesh->RemoveElement( elem );
2959     }
2960
2961     // Quadratic quadrangle
2962
2963     else if ( elem->NbNodes() >= 8 )
2964     {
2965       // get surface elem is on
2966       int aShapeId = FindShape( elem );
2967       if ( aShapeId != helper.GetSubShapeID() ) {
2968         surface.Nullify();
2969         TopoDS_Shape shape;
2970         if ( aShapeId > 0 )
2971           shape = aMesh->IndexToShape( aShapeId );
2972         if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2973           TopoDS_Face face = TopoDS::Face( shape );
2974           surface = BRep_Tool::Surface( face );
2975           if ( !surface.IsNull() )
2976             helper.SetSubShape( shape );
2977         }
2978       }
2979
2980       const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2981       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2982       for ( int i = 0; itN->more(); ++i )
2983         aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2984
2985       const SMDS_MeshNode* centrNode = aNodes[8];
2986       if ( centrNode == 0 )
2987       {
2988         centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2989                                            aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2990                                            surface.IsNull() );
2991         myLastCreatedNodes.push_back(centrNode);
2992       }
2993
2994       // create a new element
2995       const SMDS_MeshElement* newElem1 = 0;
2996       const SMDS_MeshElement* newElem2 = 0;
2997       if ( the13Diag ) {
2998         newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2999                                   aNodes[6], aNodes[7], centrNode );
3000         newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3001                                   centrNode, aNodes[4], aNodes[5] );
3002       }
3003       else {
3004         newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3005                                   aNodes[7], aNodes[4], centrNode );
3006         newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3007                                   centrNode, aNodes[5], aNodes[6] );
3008       }
3009       myLastCreatedElems.push_back(newElem1);
3010       myLastCreatedElems.push_back(newElem2);
3011       // put a new triangle on the same shape and add to the same groups
3012       if ( aShapeId )
3013       {
3014         aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3015         aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3016       }
3017       AddToSameGroups( newElem1, elem, aMesh );
3018       AddToSameGroups( newElem2, elem, aMesh );
3019       aMesh->RemoveElement( elem );
3020     }
3021   }
3022
3023   return true;
3024 }
3025
3026 //=======================================================================
3027 //function : getAngle
3028 //purpose  :
3029 //=======================================================================
3030
3031 double getAngle(const SMDS_MeshElement * tr1,
3032                 const SMDS_MeshElement * tr2,
3033                 const SMDS_MeshNode *    n1,
3034                 const SMDS_MeshNode *    n2)
3035 {
3036   double angle = 2. * M_PI; // bad angle
3037
3038   // get normals
3039   SMESH::Controls::TSequenceOfXYZ P1, P2;
3040   if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3041        !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3042     return angle;
3043   gp_Vec N1,N2;
3044   if(!tr1->IsQuadratic())
3045     N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3046   else
3047     N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3048   if ( N1.SquareMagnitude() <= gp::Resolution() )
3049     return angle;
3050   if(!tr2->IsQuadratic())
3051     N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3052   else
3053     N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3054   if ( N2.SquareMagnitude() <= gp::Resolution() )
3055     return angle;
3056
3057   // find the first diagonal node n1 in the triangles:
3058   // take in account a diagonal link orientation
3059   const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3060   for ( int t = 0; t < 2; t++ ) {
3061     SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3062     int i = 0, iDiag = -1;
3063     while ( it->more()) {
3064       const SMDS_MeshElement *n = it->next();
3065       if ( n == n1 || n == n2 ) {
3066         if ( iDiag < 0)
3067           iDiag = i;
3068         else {
3069           if ( i - iDiag == 1 )
3070             nFirst[ t ] = ( n == n1 ? n2 : n1 );
3071           else
3072             nFirst[ t ] = n;
3073           break;
3074         }
3075       }
3076       i++;
3077     }
3078   }
3079   if ( nFirst[ 0 ] == nFirst[ 1 ] )
3080     N2.Reverse();
3081
3082   angle = N1.Angle( N2 );
3083   //SCRUTE( angle );
3084   return angle;
3085 }
3086
3087 // =================================================
3088 // class generating a unique ID for a pair of nodes
3089 // and able to return nodes by that ID
3090 // =================================================
3091 class LinkID_Gen {
3092 public:
3093
3094   LinkID_Gen( const SMESHDS_Mesh* theMesh )
3095     :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3096   {}
3097
3098   long GetLinkID (const SMDS_MeshNode * n1,
3099                   const SMDS_MeshNode * n2) const
3100   {
3101     return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3102   }
3103
3104   bool GetNodes (const long             theLinkID,
3105                  const SMDS_MeshNode* & theNode1,
3106                  const SMDS_MeshNode* & theNode2) const
3107   {
3108     theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3109     if ( !theNode1 ) return false;
3110     theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3111     if ( !theNode2 ) return false;
3112     return true;
3113   }
3114
3115 private:
3116   LinkID_Gen();
3117   const SMESHDS_Mesh* myMesh;
3118   long                myMaxID;
3119 };
3120
3121
3122 //=======================================================================
3123 //function : TriToQuad
3124 //purpose  : Fuse neighbour triangles into quadrangles.
3125 //           theCrit is used to select a neighbour to fuse with.
3126 //           theMaxAngle is a max angle between element normals at which
3127 //           fusion is still performed.
3128 //=======================================================================
3129
3130 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet &                   theElems,
3131                                   SMESH::Controls::NumericalFunctorPtr theCrit,
3132                                   const double                         theMaxAngle)
3133 {
3134   ClearLastCreated();
3135   myLastCreatedElems.reserve( theElems.size() / 2 );
3136
3137   if ( !theCrit.get() )
3138     return false;
3139
3140   SMESHDS_Mesh * aMesh = GetMeshDS();
3141
3142   // Prepare data for algo: build
3143   // 1. map of elements with their linkIDs
3144   // 2. map of linkIDs with their elements
3145
3146   map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3147   map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3148   map< const SMDS_MeshElement*, set< SMESH_TLink > >  mapEl_setLi;
3149   map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3150
3151   TIDSortedElemSet::iterator itElem;
3152   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3153   {
3154     const SMDS_MeshElement* elem = *itElem;
3155     if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3156     bool IsTria = ( elem->NbCornerNodes()==3 );
3157     if (!IsTria) continue;
3158
3159     // retrieve element nodes
3160     const SMDS_MeshNode* aNodes [4];
3161     SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3162     int i = 0;
3163     while ( i < 3 )
3164       aNodes[ i++ ] = itN->next();
3165     aNodes[ 3 ] = aNodes[ 0 ];
3166
3167     // fill maps
3168     for ( i = 0; i < 3; i++ ) {
3169       SMESH_TLink link( aNodes[i], aNodes[i+1] );
3170       // check if elements sharing a link can be fused
3171       itLE = mapLi_listEl.find( link );
3172       if ( itLE != mapLi_listEl.end() ) {
3173         if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3174           continue;
3175         const SMDS_MeshElement* elem2 = (*itLE).second.front();
3176         //if ( FindShape( elem ) != FindShape( elem2 ))
3177         //  continue; // do not fuse triangles laying on different shapes
3178         if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3179           continue; // avoid making badly shaped quads
3180         (*itLE).second.push_back( elem );
3181       }
3182       else {
3183         mapLi_listEl[ link ].push_back( elem );
3184       }
3185       mapEl_setLi [ elem ].insert( link );
3186     }
3187   }
3188   // Clean the maps from the links shared by a sole element, ie
3189   // links to which only one element is bound in mapLi_listEl
3190
3191   for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3192     int nbElems = (*itLE).second.size();
3193     if ( nbElems < 2  ) {
3194       const SMDS_MeshElement* elem = (*itLE).second.front();
3195       SMESH_TLink link = (*itLE).first;
3196       mapEl_setLi[ elem ].erase( link );
3197       if ( mapEl_setLi[ elem ].empty() )
3198         mapEl_setLi.erase( elem );
3199     }
3200   }
3201
3202   // Algo: fuse triangles into quadrangles
3203
3204   while ( ! mapEl_setLi.empty() ) {
3205     // Look for the start element:
3206     // the element having the least nb of shared links
3207     const SMDS_MeshElement* startElem = 0;
3208     int minNbLinks = 4;
3209     for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3210       int nbLinks = (*itEL).second.size();
3211       if ( nbLinks < minNbLinks ) {
3212         startElem = (*itEL).first;
3213         minNbLinks = nbLinks;
3214         if ( minNbLinks == 1 )
3215           break;
3216       }
3217     }
3218
3219     // search elements to fuse starting from startElem or links of elements
3220     // fused earlyer - startLinks
3221     list< SMESH_TLink > startLinks;
3222     while ( startElem || !startLinks.empty() ) {
3223       while ( !startElem && !startLinks.empty() ) {
3224         // Get an element to start, by a link
3225         SMESH_TLink linkId = startLinks.front();
3226         startLinks.pop_front();
3227         itLE = mapLi_listEl.find( linkId );
3228         if ( itLE != mapLi_listEl.end() ) {
3229           list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3230           list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3231           for ( ; itE != listElem.end() ; itE++ )
3232             if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3233               startElem = (*itE);
3234           mapLi_listEl.erase( itLE );
3235         }
3236       }
3237
3238       if ( startElem ) {
3239         // Get candidates to be fused
3240         const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3241         const SMESH_TLink *link12 = 0, *link13 = 0;
3242         startElem = 0;
3243         ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3244         set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3245         ASSERT( !setLi.empty() );
3246         set< SMESH_TLink >::iterator itLi;
3247         for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3248         {
3249           const SMESH_TLink & link = (*itLi);
3250           itLE = mapLi_listEl.find( link );
3251           if ( itLE == mapLi_listEl.end() )
3252             continue;
3253
3254           const SMDS_MeshElement* elem = (*itLE).second.front();
3255           if ( elem == tr1 )
3256             elem = (*itLE).second.back();
3257           mapLi_listEl.erase( itLE );
3258           if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3259             continue;
3260           if ( tr2 ) {
3261             tr3 = elem;
3262             link13 = &link;
3263           }
3264           else {
3265             tr2 = elem;
3266             link12 = &link;
3267           }
3268
3269           // add other links of elem to list of links to re-start from
3270           set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3271           set< SMESH_TLink >::iterator it;
3272           for ( it = links.begin(); it != links.end(); it++ ) {
3273             const SMESH_TLink& link2 = (*it);
3274             if ( link2 != link )
3275               startLinks.push_back( link2 );
3276           }
3277         }
3278
3279         // Get nodes of possible quadrangles
3280         const SMDS_MeshNode *n12 [4], *n13 [4];
3281         bool Ok12 = false, Ok13 = false;
3282         const SMDS_MeshNode *linkNode1, *linkNode2;
3283         if(tr2) {
3284           linkNode1 = link12->first;
3285           linkNode2 = link12->second;
3286           if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3287             Ok12 = true;
3288         }
3289         if(tr3) {
3290           linkNode1 = link13->first;
3291           linkNode2 = link13->second;
3292           if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3293             Ok13 = true;
3294         }
3295
3296         // Choose a pair to fuse
3297         if ( Ok12 && Ok13 ) {
3298           SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3299           SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3300           double aBadRate12 = getBadRate( &quad12, theCrit );
3301           double aBadRate13 = getBadRate( &quad13, theCrit );
3302           if (  aBadRate13 < aBadRate12 )
3303             Ok12 = false;
3304           else
3305             Ok13 = false;
3306         }
3307
3308         // Make quadrangles
3309         // and remove fused elems and remove links from the maps
3310         mapEl_setLi.erase( tr1 );
3311         if ( Ok12 )
3312         {
3313           mapEl_setLi.erase( tr2 );
3314           mapLi_listEl.erase( *link12 );
3315           if ( tr1->NbNodes() == 3 )
3316           {
3317             const SMDS_MeshElement* newElem = 0;
3318             newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3319             myLastCreatedElems.push_back(newElem);
3320             AddToSameGroups( newElem, tr1, aMesh );
3321             int aShapeId = tr1->getshapeId();
3322             if ( aShapeId )
3323               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3324             aMesh->RemoveElement( tr1 );
3325             aMesh->RemoveElement( tr2 );
3326           }
3327           else {
3328             vector< const SMDS_MeshNode* > N1;
3329             vector< const SMDS_MeshNode* > N2;
3330             getNodesFromTwoTria(tr1,tr2,N1,N2);
3331             // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3332             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3333             // i.e. first nodes from both arrays form a new diagonal
3334             const SMDS_MeshNode* aNodes[8];
3335             aNodes[0] = N1[0];
3336             aNodes[1] = N1[1];
3337             aNodes[2] = N2[0];
3338             aNodes[3] = N2[1];
3339             aNodes[4] = N1[3];
3340             aNodes[5] = N2[5];
3341             aNodes[6] = N2[3];
3342             aNodes[7] = N1[5];
3343             const SMDS_MeshElement* newElem = 0;
3344             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3345               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3346                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3347             else
3348               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3349                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3350             myLastCreatedElems.push_back(newElem);
3351             AddToSameGroups( newElem, tr1, aMesh );
3352             int aShapeId = tr1->getshapeId();
3353             if ( aShapeId )
3354               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3355             aMesh->RemoveElement( tr1 );
3356             aMesh->RemoveElement( tr2 );
3357             // remove middle node (9)
3358             if ( N1[4]->NbInverseElements() == 0 )
3359               aMesh->RemoveNode( N1[4] );
3360             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3361               aMesh->RemoveNode( N1[6] );
3362             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3363               aMesh->RemoveNode( N2[6] );
3364           }
3365         }
3366         else if ( Ok13 )
3367         {
3368           mapEl_setLi.erase( tr3 );
3369           mapLi_listEl.erase( *link13 );
3370           if ( tr1->NbNodes() == 3 ) {
3371             const SMDS_MeshElement* newElem = 0;
3372             newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3373             myLastCreatedElems.push_back(newElem);
3374             AddToSameGroups( newElem, tr1, aMesh );
3375             int aShapeId = tr1->getshapeId();
3376             if ( aShapeId )
3377               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3378             aMesh->RemoveElement( tr1 );
3379             aMesh->RemoveElement( tr3 );
3380           }
3381           else {
3382             vector< const SMDS_MeshNode* > N1;
3383             vector< const SMDS_MeshNode* > N2;
3384             getNodesFromTwoTria(tr1,tr3,N1,N2);
3385             // now we receive following N1 and N2 (using numeration as above image)
3386             // tria1 : (1 2 4 5 9 7)  and  tria2 : (3 4 2 8 9 6)
3387             // i.e. first nodes from both arrays form a new diagonal
3388             const SMDS_MeshNode* aNodes[8];
3389             aNodes[0] = N1[0];
3390             aNodes[1] = N1[1];
3391             aNodes[2] = N2[0];
3392             aNodes[3] = N2[1];
3393             aNodes[4] = N1[3];
3394             aNodes[5] = N2[5];
3395             aNodes[6] = N2[3];
3396             aNodes[7] = N1[5];
3397             const SMDS_MeshElement* newElem = 0;
3398             if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3399               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3400                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3401             else
3402               newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3403                                        aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3404             myLastCreatedElems.push_back(newElem);
3405             AddToSameGroups( newElem, tr1, aMesh );
3406             int aShapeId = tr1->getshapeId();
3407             if ( aShapeId )
3408               aMesh->SetMeshElementOnShape( newElem, aShapeId );
3409             aMesh->RemoveElement( tr1 );
3410             aMesh->RemoveElement( tr3 );
3411             // remove middle node (9)
3412             if ( N1[4]->NbInverseElements() == 0 )
3413               aMesh->RemoveNode( N1[4] );
3414             if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3415               aMesh->RemoveNode( N1[6] );
3416             if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3417               aMesh->RemoveNode( N2[6] );
3418           }
3419         }
3420
3421         // Next element to fuse: the rejected one
3422         if ( tr3 )
3423           startElem = Ok12 ? tr3 : tr2;
3424
3425       } // if ( startElem )
3426     } // while ( startElem || !startLinks.empty() )
3427   } // while ( ! mapEl_setLi.empty() )
3428
3429   return true;
3430 }
3431
3432 //================================================================================
3433 /*!
3434  * \brief Return nodes linked to the given one
3435  * \param theNode - the node
3436  * \param linkedNodes - the found nodes
3437  * \param type - the type of elements to check
3438  *
3439  * Medium nodes are ignored
3440  */
3441 //================================================================================
3442
3443 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3444                                        TIDSortedElemSet &   linkedNodes,
3445                                        SMDSAbs_ElementType  type )
3446 {
3447   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3448   while ( elemIt->more() )
3449   {
3450     const SMDS_MeshElement* elem = elemIt->next();
3451     if(elem->GetType() == SMDSAbs_0DElement)
3452       continue;
3453
3454     SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3455     if ( elem->GetType() == SMDSAbs_Volume )
3456     {
3457       SMDS_VolumeTool vol( elem );
3458       while ( nodeIt->more() ) {
3459         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3460         if ( theNode != n && vol.IsLinked( theNode, n ))
3461           linkedNodes.insert( n );
3462       }
3463     }
3464     else
3465     {
3466       for ( int i = 0; nodeIt->more(); ++i ) {
3467         const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3468         if ( n == theNode ) {
3469           int iBefore = i - 1;
3470           int iAfter  = i + 1;
3471           if ( elem->IsQuadratic() ) {
3472             int nb = elem->NbNodes() / 2;
3473             iAfter  = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3474             iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3475           }
3476           linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3477           linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3478         }
3479       }
3480     }
3481   }
3482 }
3483
3484 //=======================================================================
3485 //function : laplacianSmooth
3486 //purpose  : pulls theNode toward the center of surrounding nodes directly
3487 //           connected to that node along an element edge
3488 //=======================================================================
3489
3490 void laplacianSmooth(const SMDS_MeshNode*                 theNode,
3491                      const Handle(Geom_Surface)&          theSurface,
3492                      map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3493 {
3494   // find surrounding nodes
3495
3496   TIDSortedElemSet nodeSet;
3497   SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3498
3499   // compute new coodrs
3500
3501   double coord[] = { 0., 0., 0. };
3502   TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3503   for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3504     const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3505     if ( theSurface.IsNull() ) { // smooth in 3D
3506       coord[0] += node->X();
3507       coord[1] += node->Y();
3508       coord[2] += node->Z();
3509     }
3510     else { // smooth in 2D
3511       ASSERT( theUVMap.find( node ) != theUVMap.end() );
3512       gp_XY* uv = theUVMap[ node ];
3513       coord[0] += uv->X();
3514       coord[1] += uv->Y();
3515     }
3516   }
3517   int nbNodes = nodeSet.size();
3518   if ( !nbNodes )
3519     return;
3520   coord[0] /= nbNodes;
3521   coord[1] /= nbNodes;
3522
3523   if ( !theSurface.IsNull() ) {
3524     ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3525     theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3526     gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3527     coord[0] = p3d.X();
3528     coord[1] = p3d.Y();
3529     coord[2] = p3d.Z();
3530   }
3531   else
3532     coord[2] /= nbNodes;
3533
3534   // move node
3535
3536   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3537 }
3538
3539 //=======================================================================
3540 //function : centroidalSmooth
3541 //purpose  : pulls theNode toward the element-area-weighted centroid of the
3542 //           surrounding elements
3543 //=======================================================================
3544
3545 void centroidalSmooth(const SMDS_MeshNode*                 theNode,
3546                       const Handle(Geom_Surface)&          theSurface,
3547                       map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3548 {
3549   gp_XYZ aNewXYZ(0.,0.,0.);
3550   SMESH::Controls::Area anAreaFunc;
3551   double totalArea = 0.;
3552   int nbElems = 0;
3553
3554   // compute new XYZ
3555
3556   SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3557   while ( elemIt->more() )
3558   {
3559     const SMDS_MeshElement* elem = elemIt->next();
3560     nbElems++;
3561
3562     gp_XYZ elemCenter(0.,0.,0.);
3563     SMESH::Controls::TSequenceOfXYZ aNodePoints;
3564     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3565     int nn = elem->NbNodes();
3566     if(elem->IsQuadratic()) nn = nn/2;
3567     int i=0;
3568     //while ( itN->more() ) {
3569     while ( i<nn ) {
3570       const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3571       i++;
3572       gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3573       aNodePoints.push_back( aP );
3574       if ( !theSurface.IsNull() ) { // smooth in 2D
3575         ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3576         gp_XY* uv = theUVMap[ aNode ];
3577         aP.SetCoord( uv->X(), uv->Y(), 0. );
3578       }
3579       elemCenter += aP;
3580     }
3581     double elemArea = anAreaFunc.GetValue( aNodePoints );
3582     totalArea += elemArea;
3583     elemCenter /= nn;
3584     aNewXYZ += elemCenter * elemArea;
3585   }
3586   aNewXYZ /= totalArea;
3587   if ( !theSurface.IsNull() ) {
3588     theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3589     aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3590   }
3591
3592   // move node
3593
3594   const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3595 }
3596
3597 //=======================================================================
3598 //function : getClosestUV
3599 //purpose  : return UV of closest projection
3600 //=======================================================================
3601
3602 static bool getClosestUV (Extrema_GenExtPS& projector,
3603                           const gp_Pnt&     point,
3604                           gp_XY &           result)
3605 {
3606   projector.Perform( point );
3607   if ( projector.IsDone() ) {
3608     double u, v, minVal = DBL_MAX;
3609     for ( int i = projector.NbExt(); i > 0; i-- )
3610       if ( projector.SquareDistance( i ) < minVal ) {
3611         minVal = projector.SquareDistance( i );
3612         projector.Point( i ).Parameter( u, v );
3613       }
3614     result.SetCoord( u, v );
3615     return true;
3616   }
3617   return false;
3618 }
3619
3620 //=======================================================================
3621 //function : Smooth
3622 //purpose  : Smooth theElements during theNbIterations or until a worst
3623 //           element has aspect ratio <= theTgtAspectRatio.
3624 //           Aspect Ratio varies in range [1.0, inf].
3625 //           If theElements is empty, the whole mesh is smoothed.
3626 //           theFixedNodes contains additionally fixed nodes. Nodes built
3627 //           on edges and boundary nodes are always fixed.
3628 //=======================================================================
3629
3630 void SMESH_MeshEditor::Smooth (TIDSortedElemSet &          theElems,
3631                                set<const SMDS_MeshNode*> & theFixedNodes,
3632                                const SmoothMethod          theSmoothMethod,
3633                                const int                   theNbIterations,
3634                                double                      theTgtAspectRatio,
3635                                const bool                  the2D)
3636 {
3637   ClearLastCreated();
3638
3639   if ( theTgtAspectRatio < 1.0 )
3640     theTgtAspectRatio = 1.0;
3641
3642   const double disttol = 1.e-16;
3643
3644   SMESH::Controls::AspectRatio aQualityFunc;
3645
3646   SMESHDS_Mesh* aMesh = GetMeshDS();
3647
3648   if ( theElems.empty() ) {
3649     // add all faces to theElems
3650     SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3651     while ( fIt->more() ) {
3652       const SMDS_MeshElement* face = fIt->next();
3653       theElems.insert( theElems.end(), face );
3654     }
3655   }
3656   // get all face ids theElems are on
3657   set< int > faceIdSet;
3658   TIDSortedElemSet::iterator itElem;
3659   if ( the2D )
3660     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3661       int fId = FindShape( *itElem );
3662       // check that corresponding submesh exists and a shape is face
3663       if (fId &&
3664           faceIdSet.find( fId ) == faceIdSet.end() &&
3665           aMesh->MeshElements( fId )) {
3666         TopoDS_Shape F = aMesh->IndexToShape( fId );
3667         if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3668           faceIdSet.insert( fId );
3669       }
3670     }
3671   faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3672
3673   // ===============================================
3674   // smooth elements on each TopoDS_Face separately
3675   // ===============================================
3676
3677   SMESH_MesherHelper helper( *GetMesh() );
3678
3679   set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3680   for ( ; fId != faceIdSet.rend(); ++fId )
3681   {
3682     // get face surface and submesh
3683     Handle(Geom_Surface) surface;
3684     SMESHDS_SubMesh* faceSubMesh = 0;
3685     TopoDS_Face face;
3686     double fToler2 = 0;
3687     double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3688     bool isUPeriodic = false, isVPeriodic = false;
3689     if ( *fId )
3690     {
3691       face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3692       surface = BRep_Tool::Surface( face );
3693       faceSubMesh = aMesh->MeshElements( *fId );
3694       fToler2 = BRep_Tool::Tolerance( face );
3695       fToler2 *= fToler2 * 10.;
3696       isUPeriodic = surface->IsUPeriodic();
3697       // if ( isUPeriodic )
3698       //   surface->UPeriod();
3699       isVPeriodic = surface->IsVPeriodic();
3700       // if ( isVPeriodic )
3701       //   surface->VPeriod();
3702       surface->Bounds( u1, u2, v1, v2 );
3703       helper.SetSubShape( face );
3704     }
3705     // ---------------------------------------------------------
3706     // for elements on a face, find movable and fixed nodes and
3707     // compute UV for them
3708     // ---------------------------------------------------------
3709     bool checkBoundaryNodes = false;
3710     bool isQuadratic = false;
3711     set<const SMDS_MeshNode*> setMovableNodes;
3712     map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3713     list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3714     list< const SMDS_MeshElement* > elemsOnFace;
3715
3716     Extrema_GenExtPS projector;
3717     GeomAdaptor_Surface surfAdaptor;
3718     if ( !surface.IsNull() ) {
3719       surfAdaptor.Load( surface );
3720       projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3721     }
3722     int nbElemOnFace = 0;
3723     itElem = theElems.begin();
3724     // loop on not yet smoothed elements: look for elems on a face
3725     while ( itElem != theElems.end() )
3726     {
3727       if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3728         break; // all elements found
3729
3730       const SMDS_MeshElement* elem = *itElem;
3731       if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3732            ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3733         ++itElem;
3734         continue;
3735       }
3736       elemsOnFace.push_back( elem );
3737       theElems.erase( itElem++ );
3738       nbElemOnFace++;
3739
3740       if ( !isQuadratic )
3741         isQuadratic = elem->IsQuadratic();
3742
3743       // get movable nodes of elem
3744       const SMDS_MeshNode* node;
3745       SMDS_TypeOfPosition posType;
3746       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3747       int nn = 0, nbn =  elem->NbNodes();
3748       if(elem->IsQuadratic())
3749         nbn = nbn/2;
3750       while ( nn++ < nbn ) {
3751         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3752         const SMDS_PositionPtr& pos = node->GetPosition();
3753         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3754         if (posType != SMDS_TOP_EDGE &&
3755             posType != SMDS_TOP_VERTEX &&
3756             theFixedNodes.find( node ) == theFixedNodes.end())
3757         {
3758           // check if all faces around the node are on faceSubMesh
3759           // because a node on edge may be bound to face
3760           bool all = true;
3761           if ( faceSubMesh ) {
3762             SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3763             while ( eIt->more() && all ) {
3764               const SMDS_MeshElement* e = eIt->next();
3765               all = faceSubMesh->Contains( e );
3766             }
3767           }
3768           if ( all )
3769             setMovableNodes.insert( node );
3770           else
3771             checkBoundaryNodes = true;
3772         }
3773         if ( posType == SMDS_TOP_3DSPACE )
3774           checkBoundaryNodes = true;
3775       }
3776
3777       if ( surface.IsNull() )
3778         continue;
3779
3780       // get nodes to check UV
3781       list< const SMDS_MeshNode* > uvCheckNodes;
3782       const SMDS_MeshNode* nodeInFace = 0;
3783       itN = elem->nodesIterator();
3784       nn = 0; nbn =  elem->NbNodes();
3785       if(elem->IsQuadratic())
3786         nbn = nbn/2;
3787       while ( nn++ < nbn ) {
3788         node = static_cast<const SMDS_MeshNode*>( itN->next() );
3789         if ( node->GetPosition()->GetDim() == 2 )
3790           nodeInFace = node;
3791         if ( uvMap.find( node ) == uvMap.end() )
3792           uvCheckNodes.push_back( node );
3793         // add nodes of elems sharing node
3794         //         SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3795         //         while ( eIt->more() ) {
3796         //           const SMDS_MeshElement* e = eIt->next();
3797         //           if ( e != elem ) {
3798         //             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3799         //             while ( nIt->more() ) {
3800         //               const SMDS_MeshNode* n =
3801         //                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3802         //               if ( uvMap.find( n ) == uvMap.end() )
3803         //                 uvCheckNodes.push_back( n );
3804         //             }
3805         //           }
3806         //         }
3807       }
3808       // check UV on face
3809       list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3810       for ( ; n != uvCheckNodes.end(); ++n ) {
3811         node = *n;
3812         gp_XY uv( 0, 0 );
3813         const SMDS_PositionPtr& pos = node->GetPosition();
3814         posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3815         // get existing UV
3816         if ( pos )
3817         {
3818           bool toCheck = true;
3819           uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3820         }
3821         // compute not existing UV
3822         bool project = ( posType == SMDS_TOP_3DSPACE );
3823         // double dist1 = DBL_MAX, dist2 = 0;
3824         // if ( posType != SMDS_TOP_3DSPACE ) {
3825         //   dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3826         //   project = dist1 > fToler2;
3827         // }
3828         if ( project ) { // compute new UV
3829           gp_XY newUV;
3830           gp_Pnt pNode = SMESH_TNodeXYZ( node );
3831           if ( !getClosestUV( projector, pNode, newUV )) {
3832             MESSAGE("Node Projection Failed " << node);
3833           }
3834           else {
3835             if ( isUPeriodic )
3836               newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3837             if ( isVPeriodic )
3838               newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3839             // check new UV
3840             // if ( posType != SMDS_TOP_3DSPACE )
3841             //   dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3842             // if ( dist2 < dist1 )
3843             uv = newUV;
3844           }
3845         }
3846         // store UV in the map
3847         listUV.push_back( uv );
3848         uvMap.insert( make_pair( node, &listUV.back() ));
3849       }
3850     } // loop on not yet smoothed elements
3851
3852     if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3853       checkBoundaryNodes = true;
3854
3855     // fix nodes on mesh boundary
3856
3857     if ( checkBoundaryNodes ) {
3858       map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3859       map< SMESH_TLink, int >::iterator link_nb;
3860       // put all elements links to linkNbMap
3861       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3862       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3863         const SMDS_MeshElement* elem = (*elemIt);
3864         int nbn =  elem->NbCornerNodes();
3865         // loop on elem links: insert them in linkNbMap
3866         for ( int iN = 0; iN < nbn; ++iN ) {
3867           const SMDS_MeshNode* n1 = elem->GetNode( iN );
3868           const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3869           SMESH_TLink link( n1, n2 );
3870           link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3871           link_nb->second++;
3872         }
3873       }
3874       // remove nodes that are in links encountered only once from setMovableNodes
3875       for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3876         if ( link_nb->second == 1 ) {
3877           setMovableNodes.erase( link_nb->first.node1() );
3878           setMovableNodes.erase( link_nb->first.node2() );
3879         }
3880       }
3881     }
3882
3883     // -----------------------------------------------------
3884     // for nodes on seam edge, compute one more UV ( uvMap2 );
3885     // find movable nodes linked to nodes on seam and which
3886     // are to be smoothed using the second UV ( uvMap2 )
3887     // -----------------------------------------------------
3888
3889     set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3890     if ( !surface.IsNull() ) {
3891       TopExp_Explorer eExp( face, TopAbs_EDGE );
3892       for ( ; eExp.More(); eExp.Next() ) {
3893         TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3894         if ( !BRep_Tool::IsClosed( edge, face ))
3895           continue;
3896         SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3897         if ( !sm ) continue;
3898         // find out which parameter varies for a node on seam
3899         double f,l;
3900         gp_Pnt2d uv1, uv2;
3901         Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3902         if ( pcurve.IsNull() ) continue;
3903         uv1 = pcurve->Value( f );
3904         edge.Reverse();
3905         pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3906         if ( pcurve.IsNull() ) continue;
3907         uv2 = pcurve->Value( f );
3908         int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3909         // assure uv1 < uv2
3910         if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3911           std::swap( uv1, uv2 );
3912         // get nodes on seam and its vertices
3913         list< const SMDS_MeshNode* > seamNodes;
3914         SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3915         while ( nSeamIt->more() ) {
3916           const SMDS_MeshNode* node = nSeamIt->next();
3917           if ( !isQuadratic || !IsMedium( node ))
3918             seamNodes.push_back( node );
3919         }
3920         TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3921         for ( ; vExp.More(); vExp.Next() ) {
3922           sm = aMesh->MeshElements( vExp.Current() );
3923           if ( sm ) {
3924             nSeamIt = sm->GetNodes();
3925             while ( nSeamIt->more() )
3926               seamNodes.push_back( nSeamIt->next() );
3927           }
3928         }
3929         // loop on nodes on seam
3930         list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3931         for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3932           const SMDS_MeshNode* nSeam = *noSeIt;
3933           map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3934           if ( n_uv == uvMap.end() )
3935             continue;
3936           // set the first UV
3937           n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3938           // set the second UV
3939           listUV.push_back( *n_uv->second );
3940           listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3941           if ( uvMap2.empty() )
3942             uvMap2 = uvMap; // copy the uvMap contents
3943           uvMap2[ nSeam ] = &listUV.back();
3944
3945           // collect movable nodes linked to ones on seam in nodesNearSeam
3946           SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3947           while ( eIt->more() ) {
3948             const SMDS_MeshElement* e = eIt->next();
3949             int nbUseMap1 = 0, nbUseMap2 = 0;
3950             SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3951             int nn = 0, nbn =  e->NbNodes();
3952             if(e->IsQuadratic()) nbn = nbn/2;
3953             while ( nn++ < nbn )
3954             {
3955               const SMDS_MeshNode* n =
3956                 static_cast<const SMDS_MeshNode*>( nIt->next() );
3957               if (n == nSeam ||
3958                   setMovableNodes.find( n ) == setMovableNodes.end() )
3959                 continue;
3960               // add only nodes being closer to uv2 than to uv1
3961               // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3962               //              0.5 * ( n->Y() + nSeam->Y() ),
3963               //              0.5 * ( n->Z() + nSeam->Z() ));
3964               // gp_XY uv;
3965               // getClosestUV( projector, pMid, uv );
3966               double x = uvMap[ n ]->Coord( iPar );
3967               if ( Abs( uv1.Coord( iPar ) - x ) >
3968                    Abs( uv2.Coord( iPar ) - x )) {
3969                 nodesNearSeam.insert( n );
3970                 nbUseMap2++;
3971               }
3972               else
3973                 nbUseMap1++;
3974             }
3975             // for centroidalSmooth all element nodes must
3976             // be on one side of a seam
3977             if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3978               SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3979               nn = 0;
3980               while ( nn++ < nbn ) {
3981                 const SMDS_MeshNode* n =
3982                   static_cast<const SMDS_MeshNode*>( nIt->next() );
3983                 setMovableNodes.erase( n );
3984               }
3985             }
3986           }
3987         } // loop on nodes on seam
3988       } // loop on edge of a face
3989     } // if ( !face.IsNull() )
3990
3991     if ( setMovableNodes.empty() ) {
3992       MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3993       continue; // goto next face
3994     }
3995
3996     // -------------
3997     // SMOOTHING //
3998     // -------------
3999
4000     int it = -1;
4001     double maxRatio = -1., maxDisplacement = -1.;
4002     set<const SMDS_MeshNode*>::iterator nodeToMove;
4003     for ( it = 0; it < theNbIterations; it++ ) {
4004       maxDisplacement = 0.;
4005       nodeToMove = setMovableNodes.begin();
4006       for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4007         const SMDS_MeshNode* node = (*nodeToMove);
4008         gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4009
4010         // smooth
4011         bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4012         if ( theSmoothMethod == LAPLACIAN )
4013           laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4014         else
4015           centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4016
4017         // node displacement
4018         gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4019         Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4020         if ( aDispl > maxDisplacement )
4021           maxDisplacement = aDispl;
4022       }
4023       // no node movement => exit
4024       //if ( maxDisplacement < 1.e-16 ) {
4025       if ( maxDisplacement < disttol ) {
4026         MESSAGE("-- no node movement --");
4027         break;
4028       }
4029
4030       // check elements quality
4031       maxRatio  = 0;
4032       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4033       for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4034         const SMDS_MeshElement* elem = (*elemIt);
4035         if ( !elem || elem->GetType() != SMDSAbs_Face )
4036           continue;
4037         SMESH::Controls::TSequenceOfXYZ aPoints;
4038         if ( aQualityFunc.GetPoints( elem, aPoints )) {
4039           double aValue = aQualityFunc.GetValue( aPoints );
4040           if ( aValue > maxRatio )
4041             maxRatio = aValue;
4042         }
4043       }
4044       if ( maxRatio <= theTgtAspectRatio ) {
4045         MESSAGE("-- quality achived --");
4046         break;
4047       }
4048       if (it+1 == theNbIterations) {
4049         MESSAGE("-- Iteration limit exceeded --");
4050       }
4051     } // smoothing iterations
4052
4053     MESSAGE(" Face id: " << *fId <<
4054             " Nb iterstions: " << it <<
4055             " Displacement: " << maxDisplacement <<
4056             " Aspect Ratio " << maxRatio);
4057
4058     // ---------------------------------------
4059     // new nodes positions are computed,
4060     // record movement in DS and set new UV
4061     // ---------------------------------------
4062     nodeToMove = setMovableNodes.begin();
4063     for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4064       SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4065       aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4066       map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4067       if ( node_uv != uvMap.end() ) {
4068         gp_XY* uv = node_uv->second;
4069         node->SetPosition
4070           ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4071       }
4072     }
4073
4074     // move medium nodes of quadratic elements
4075     if ( isQuadratic )
4076     {
4077       vector<const SMDS_MeshNode*> nodes;
4078       bool checkUV;
4079       list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4080       for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4081       {
4082         const SMDS_MeshElement* QF = *elemIt;
4083         if ( QF->IsQuadratic() )
4084         {
4085           nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4086                         SMDS_MeshElement::iterator() );
4087           nodes.push_back( nodes[0] );
4088           gp_Pnt xyz;
4089           for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4090           {
4091             if ( !surface.IsNull() )
4092             {
4093               gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4094               gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4095               gp_XY uv  = helper.GetMiddleUV( surface, uv1, uv2 );
4096               xyz = surface->Value( uv.X(), uv.Y() );
4097             }
4098             else {
4099               xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4100             }
4101             if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4102               // we have to move a medium node
4103               aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4104           }
4105         }
4106       }
4107     }
4108
4109   } // loop on face ids
4110
4111 }
4112
4113 namespace
4114 {
4115   //=======================================================================
4116   //function : isReverse
4117   //purpose  : Return true if normal of prevNodes is not co-directied with
4118   //           gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4119   //           iNotSame is where prevNodes and nextNodes are different.
4120   //           If result is true then future volume orientation is OK
4121   //=======================================================================
4122
4123   bool isReverse(const SMDS_MeshElement*             face,
4124                  const vector<const SMDS_MeshNode*>& prevNodes,
4125                  const vector<const SMDS_MeshNode*>& nextNodes,
4126                  const int                           iNotSame)
4127   {
4128
4129     SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4130     SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4131     gp_XYZ extrDir( pN - pP ), faceNorm;
4132     SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4133
4134     return faceNorm * extrDir < 0.0;
4135   }
4136
4137   //================================================================================
4138   /*!
4139    * \brief Assure that theElemSets[0] holds elements, not nodes
4140    */
4141   //================================================================================
4142
4143   void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4144   {
4145     if ( !theElemSets[0].empty() &&
4146          (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4147     {
4148       std::swap( theElemSets[0], theElemSets[1] );
4149     }
4150     else if ( !theElemSets[1].empty() &&
4151               (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4152     {
4153       std::swap( theElemSets[0], theElemSets[1] );
4154     }
4155   }
4156 }
4157
4158 //=======================================================================
4159 /*!
4160  * \brief Create elements by sweeping an element
4161  * \param elem - element to sweep
4162  * \param newNodesItVec - nodes generated from each node of the element
4163  * \param newElems - generated elements
4164  * \param nbSteps - number of sweeping steps
4165  * \param srcElements - to append elem for each generated element
4166  */
4167 //=======================================================================
4168
4169 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement*               elem,
4170                                     const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4171                                     list<const SMDS_MeshElement*>&        newElems,
4172                                     const size_t                          nbSteps,
4173                                     SMESH_SequenceOfElemPtr&              srcElements)
4174 {
4175   SMESHDS_Mesh* aMesh = GetMeshDS();
4176
4177   const int           nbNodes = elem->NbNodes();
4178   const int         nbCorners = elem->NbCornerNodes();
4179   SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4180                                                           polyhedron creation !!! */
4181   // Loop on elem nodes:
4182   // find new nodes and detect same nodes indices
4183   vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4184   vector<const SMDS_MeshNode*> prevNod( nbNodes );
4185   vector<const SMDS_MeshNode*> nextNod( nbNodes );
4186   vector<const SMDS_MeshNode*> midlNod( nbNodes );
4187
4188   int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4189   vector<int> sames(nbNodes);
4190   vector<bool> isSingleNode(nbNodes);
4191
4192   for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4193     TNodeOfNodeListMapItr                        nnIt = newNodesItVec[ iNode ];
4194     const SMDS_MeshNode*                         node = nnIt->first;
4195     const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4196     if ( listNewNodes.empty() )
4197       return;
4198
4199     itNN   [ iNode ] = listNewNodes.begin();
4200     prevNod[ iNode ] = node;
4201     nextNod[ iNode ] = listNewNodes.front();
4202
4203     isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4204                                                              corner node of linear */
4205     if ( prevNod[ iNode ] != nextNod [ iNode ])
4206       nbDouble += !isSingleNode[iNode];
4207
4208     if( iNode < nbCorners ) { // check corners only
4209       if ( prevNod[ iNode ] == nextNod [ iNode ])
4210         sames[nbSame++] = iNode;
4211       else
4212         iNotSameNode = iNode;
4213     }
4214   }
4215
4216   if ( nbSame == nbNodes || nbSame > 2) {
4217     MESSAGE( " Too many same nodes of element " << elem->GetID() );
4218     return;
4219   }
4220
4221   if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4222   {
4223     // fix nodes order to have bottom normal external
4224     if ( baseType == SMDSEntity_Polygon )
4225     {
4226       std::reverse( itNN.begin(), itNN.end() );
4227       std::reverse( prevNod.begin(), prevNod.end() );
4228       std::reverse( midlNod.begin(), midlNod.end() );
4229       std::reverse( nextNod.begin(), nextNod.end() );
4230       std::reverse( isSingleNode.begin(), isSingleNode.end() );
4231     }
4232     else
4233     {
4234       const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4235       SMDS_MeshCell::applyInterlace( ind, itNN );
4236       SMDS_MeshCell::applyInterlace( ind, prevNod );
4237       SMDS_MeshCell::applyInterlace( ind, nextNod );
4238       SMDS_MeshCell::applyInterlace( ind, midlNod );
4239       SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4240       if ( nbSame > 0 )
4241       {
4242         sames[nbSame] = iNotSameNode;
4243         for ( int j = 0; j <= nbSame; ++j )
4244           for ( size_t i = 0; i < ind.size(); ++i )
4245             if ( ind[i] == sames[j] )
4246             {
4247               sames[j] = i;
4248               break;
4249             }
4250         iNotSameNode = sames[nbSame];
4251       }
4252     }
4253   }
4254   else if ( elem->GetType() == SMDSAbs_Edge )
4255   {
4256     // orient a new face same as adjacent one
4257     int i1, i2;
4258     const SMDS_MeshElement* e;
4259     TIDSortedElemSet dummy;
4260     if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4261         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4262         ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4263     {
4264       // there is an adjacent face, check order of nodes in it
4265       bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4266       if ( sameOrder )
4267       {
4268         std::swap( itNN[0],    itNN[1] );
4269         std::swap( prevNod[0], prevNod[1] );
4270         std::swap( nextNod[0], nextNod[1] );
4271         isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4272         if ( nbSame > 0 )
4273           sames[0] = 1 - sames[0];
4274         iNotSameNode = 1 - iNotSameNode;
4275       }
4276     }
4277   }
4278
4279   int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4280   if ( nbSame > 0 ) {
4281     iSameNode    = sames[ nbSame-1 ];
4282     iBeforeSame  = ( iSameNode + nbCorners - 1 ) % nbCorners;
4283     iAfterSame   = ( iSameNode + 1 ) % nbCorners;
4284     iOpposSame   = ( iSameNode - 2 < 0  ? iSameNode + 2 : iSameNode - 2 );
4285   }
4286
4287   if ( baseType == SMDSEntity_Polygon )
4288   {
4289     if      ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4290     else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4291   }
4292   else if ( baseType == SMDSEntity_Quad_Polygon )
4293   {
4294     if      ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4295     else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4296   }
4297
4298   // make new elements
4299   for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4300   {
4301     // get next nodes
4302     for ( iNode = 0; iNode < nbNodes; iNode++ )
4303     {
4304       midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4305       nextNod[ iNode ] = *itNN[ iNode ]++;
4306     }
4307
4308     SMDS_MeshElement* aNewElem = 0;
4309     /*if(!elem->IsPoly())*/ {
4310       switch ( baseType ) {
4311       case SMDSEntity_0D:
4312       case SMDSEntity_Node: { // sweep NODE
4313         if ( nbSame == 0 ) {
4314           if ( isSingleNode[0] )
4315             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4316           else
4317             aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4318         }
4319         else
4320           return;
4321         break;
4322       }
4323       case SMDSEntity_Edge: { // sweep EDGE
4324         if ( nbDouble == 0 )
4325         {
4326           if ( nbSame == 0 ) // ---> quadrangle
4327             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4328                                       nextNod[ 1 ], nextNod[ 0 ] );
4329           else               // ---> triangle
4330             aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4331                                       nextNod[ iNotSameNode ] );
4332         }
4333         else                 // ---> polygon
4334         {
4335           vector<const SMDS_MeshNode*> poly_nodes;
4336           poly_nodes.push_back( prevNod[0] );
4337           poly_nodes.push_back( prevNod[1] );
4338           if ( prevNod[1] != nextNod[1] )
4339           {
4340             if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4341             poly_nodes.push_back( nextNod[1] );
4342           }
4343           if ( prevNod[0] != nextNod[0] )
4344           {
4345             poly_nodes.push_back( nextNod[0] );
4346             if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4347           }
4348           switch ( poly_nodes.size() ) {
4349           case 3:
4350             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4351             break;
4352           case 4:
4353             aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4354                                        poly_nodes[ 2 ], poly_nodes[ 3 ]);
4355             break;
4356           default:
4357             aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4358           }
4359         }
4360         break;
4361       }
4362       case SMDSEntity_Triangle: // TRIANGLE --->
4363       {
4364         if ( nbDouble > 0 ) break;
4365         if ( nbSame == 0 )       // ---> pentahedron
4366           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4367                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4368
4369         else if ( nbSame == 1 )  // ---> pyramid
4370           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4371                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4372                                        nextNod[ iSameNode ]);
4373
4374         else // 2 same nodes:       ---> tetrahedron
4375           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4376                                        nextNod[ iNotSameNode ]);
4377         break;
4378       }
4379       case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4380       {
4381         if ( nbSame == 2 )
4382           return;
4383         if ( nbDouble+nbSame == 2 )
4384         {
4385           if(nbSame==0) {      // ---> quadratic quadrangle
4386             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4387                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4388           }
4389           else { //(nbSame==1) // ---> quadratic triangle
4390             if(sames[0]==2) {
4391               return; // medium node on axis
4392             }
4393             else if(sames[0]==0)
4394               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4395                                         prevNod[2], midlNod[1], nextNod[2] );
4396             else // sames[0]==1
4397               aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4398                                         prevNod[2], nextNod[2], midlNod[0]);
4399           }
4400         }
4401         else if ( nbDouble == 3 )
4402         {
4403           if ( nbSame == 0 ) {  // ---> bi-quadratic quadrangle
4404             aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4405                                       prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4406           }
4407         }
4408         else
4409           return;
4410         break;
4411       }
4412       case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4413         if ( nbDouble > 0 ) break;
4414
4415         if ( nbSame == 0 )       // ---> hexahedron
4416           aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4417                                        nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4418
4419         else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4420           aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4421                                        nextNod[ iAfterSame ],  nextNod[ iBeforeSame ],
4422                                        nextNod[ iSameNode ]);
4423           newElems.push_back( aNewElem );
4424           aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ],  prevNod[ iOpposSame ],
4425                                        prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4426                                        nextNod[ iOpposSame ],  nextNod[ iBeforeSame ] );
4427         }
4428         else if ( nbSame == 2 ) { // ---> pentahedron
4429           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4430             // iBeforeSame is same too
4431             aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4432                                          nextNod[ iOpposSame ],  prevNod[ iSameNode ],
4433                                          prevNod[ iAfterSame ],  nextNod[ iAfterSame ]);
4434           else
4435             // iAfterSame is same too
4436             aNewElem = aMesh->AddVolume (prevNod[ iSameNode ],   prevNod[ iBeforeSame ],
4437                                          nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4438                                          prevNod[ iOpposSame ],  nextNod[ iOpposSame ]);
4439         }
4440         break;
4441       }
4442       case SMDSEntity_Quad_Triangle:  // sweep (Bi)Quadratic TRIANGLE --->
4443       case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4444         if ( nbDouble+nbSame != 3 ) break;
4445         if(nbSame==0) {
4446           // --->  pentahedron with 15 nodes
4447           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4448                                        nextNod[0], nextNod[1], nextNod[2],
4449                                        prevNod[3], prevNod[4], prevNod[5],
4450                                        nextNod[3], nextNod[4], nextNod[5],
4451                                        midlNod[0], midlNod[1], midlNod[2]);
4452         }
4453         else if(nbSame==1) {
4454           // --->  2d order pyramid of 13 nodes
4455           int apex = iSameNode;
4456           int i0 = ( apex + 1 ) % nbCorners;
4457           int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4458           int i0a = apex + 3;
4459           int i1a = i1 + 3;
4460           int i01 = i0 + 3;
4461           aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4462                                       nextNod[i0], nextNod[i1], prevNod[apex],
4463                                       prevNod[i01], midlNod[i0],
4464                                       nextNod[i01], midlNod[i1],
4465                                       prevNod[i1a], prevNod[i0a],
4466                                       nextNod[i0a], nextNod[i1a]);
4467         }
4468         else if(nbSame==2) {
4469           // --->  2d order tetrahedron of 10 nodes
4470           int n1 = iNotSameNode;
4471           int n2 = ( n1 + 1             ) % nbCorners;
4472           int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4473           int n12 = n1 + 3;
4474           int n23 = n2 + 3;
4475           int n31 = n3 + 3;
4476           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4477                                        prevNod[n12], prevNod[n23], prevNod[n31],
4478                                        midlNod[n1], nextNod[n12], nextNod[n31]);
4479         }
4480         break;
4481       }
4482       case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4483         if( nbSame == 0 ) {
4484           if ( nbDouble != 4 ) break;
4485           // --->  hexahedron with 20 nodes
4486           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4487                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4488                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4489                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4490                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4491         }
4492         else if(nbSame==1) {
4493           // ---> pyramid + pentahedron - can not be created since it is needed
4494           // additional middle node at the center of face
4495           //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4496           return;
4497         }
4498         else if( nbSame == 2 ) {
4499           if ( nbDouble != 2 ) break;
4500           // --->  2d order Pentahedron with 15 nodes
4501           int n1,n2,n4,n5;
4502           if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4503             // iBeforeSame is same too
4504             n1 = iBeforeSame;
4505             n2 = iOpposSame;
4506             n4 = iSameNode;
4507             n5 = iAfterSame;
4508           }
4509           else {
4510             // iAfterSame is same too
4511             n1 = iSameNode;
4512             n2 = iBeforeSame;
4513             n4 = iAfterSame;
4514             n5 = iOpposSame;
4515           }
4516           int n12 = n2 + 4;
4517           int n45 = n4 + 4;
4518           int n14 = n1 + 4;
4519           int n25 = n5 + 4;
4520           aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4521                                        prevNod[n4], prevNod[n5], nextNod[n5],
4522                                        prevNod[n12], midlNod[n2], nextNod[n12],
4523                                        prevNod[n45], midlNod[n5], nextNod[n45],
4524                                        prevNod[n14], prevNod[n25], nextNod[n25]);
4525         }
4526         break;
4527       }
4528       case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4529
4530         if( nbSame == 0 && nbDouble == 9 ) {
4531           // --->  tri-quadratic hexahedron with 27 nodes
4532           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4533                                        nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4534                                        prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4535                                        nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4536                                        midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4537                                        prevNod[8], // bottom center
4538                                        midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4539                                        nextNod[8], // top center
4540                                        midlNod[8]);// elem center
4541         }
4542         else
4543         {
4544           return;
4545         }
4546         break;
4547       }
4548       case SMDSEntity_Polygon: { // sweep POLYGON
4549
4550         if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4551           // --->  hexagonal prism
4552           aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4553                                        prevNod[3], prevNod[4], prevNod[5],
4554                                        nextNod[0], nextNod[1], nextNod[2],
4555                                        nextNod[3], nextNod[4], nextNod[5]);
4556         }
4557         break;
4558       }
4559       case SMDSEntity_Ball:
4560         return;
4561
4562       default:
4563         break;
4564       } // switch ( baseType )
4565     } // scope
4566
4567     if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4568     {
4569       if ( baseType != SMDSEntity_Polygon )
4570       {
4571         const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4572         SMDS_MeshCell::applyInterlace( ind, prevNod );
4573         SMDS_MeshCell::applyInterlace( ind, nextNod );
4574         SMDS_MeshCell::applyInterlace( ind, midlNod );
4575         SMDS_MeshCell::applyInterlace( ind, itNN );
4576         SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4577         baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4578       }
4579       vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4580       vector<int> quantities (nbNodes + 2);
4581       polyedre_nodes.clear();
4582       quantities.clear();
4583
4584       // bottom of prism
4585       for (int inode = 0; inode < nbNodes; inode++)
4586         polyedre_nodes.push_back( prevNod[inode] );
4587       quantities.push_back( nbNodes );
4588
4589       // top of prism
4590       polyedre_nodes.push_back( nextNod[0] );
4591       for (int inode = nbNodes; inode-1; --inode )
4592         polyedre_nodes.push_back( nextNod[inode-1] );
4593       quantities.push_back( nbNodes );
4594
4595       // side faces
4596       // 3--6--2
4597       // |     |
4598       // 7     5
4599       // |     |
4600       // 0--4--1
4601       const int iQuad = elem->IsQuadratic();
4602       for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4603       {
4604         const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4605         int inextface = (iface+1+iQuad) % nbNodes;
4606         int imid      = (iface+1) % nbNodes;
4607         polyedre_nodes.push_back( prevNod[inextface] );         // 0
4608         if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4609         polyedre_nodes.push_back( prevNod[iface] );             // 1
4610         if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4611         {
4612           if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4613           polyedre_nodes.push_back( nextNod[iface] );                         // 2
4614         }
4615         if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] );               // 6
4616         if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4617         {
4618           polyedre_nodes.push_back( nextNod[inextface] );                            // 3
4619           if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4620         }
4621         const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4622         if ( nbFaceNodes > 2 )
4623           quantities.push_back( nbFaceNodes );
4624         else // degenerated face
4625           polyedre_nodes.resize( prevNbNodes );
4626       }
4627       aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4628
4629     } // try to create a polyherdal prism
4630
4631     if ( aNewElem ) {
4632       newElems.push_back( aNewElem );
4633       myLastCreatedElems.push_back(aNewElem);
4634       srcElements.push_back( elem );
4635     }
4636
4637     // set new prev nodes
4638     for ( iNode = 0; iNode < nbNodes; iNode++ )
4639       prevNod[ iNode ] = nextNod[ iNode ];
4640
4641   } // loop on steps
4642 }
4643
4644 //=======================================================================
4645 /*!
4646  * \brief Create 1D and 2D elements around swept elements
4647  * \param mapNewNodes - source nodes and ones generated from them
4648  * \param newElemsMap - source elements and ones generated from them
4649  * \param elemNewNodesMap - nodes generated from each node of each element
4650  * \param elemSet - all swept elements
4651  * \param nbSteps - number of sweeping steps
4652  * \param srcElements - to append elem for each generated element
4653  */
4654 //=======================================================================
4655
4656 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap &     mapNewNodes,
4657                                   TTElemOfElemListMap &    newElemsMap,
4658                                   TElemOfVecOfNnlmiMap &   elemNewNodesMap,
4659                                   TIDSortedElemSet&        elemSet,
4660                                   const int                nbSteps,
4661                                   SMESH_SequenceOfElemPtr& srcElements)
4662 {
4663   ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4664   SMESHDS_Mesh* aMesh = GetMeshDS();
4665
4666   // Find nodes belonging to only one initial element - sweep them into edges.
4667
4668   TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4669   for ( ; nList != mapNewNodes.end(); nList++ )
4670   {
4671     const SMDS_MeshNode* node =
4672       static_cast<const SMDS_MeshNode*>( nList->first );
4673     if ( newElemsMap.count( node ))
4674       continue; // node was extruded into edge
4675     SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4676     int nbInitElems = 0;
4677     const SMDS_MeshElement* el = 0;
4678     SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4679     while ( eIt->more() && nbInitElems < 2 ) {
4680       const SMDS_MeshElement* e = eIt->next();
4681       SMDSAbs_ElementType  type = e->GetType();
4682       if ( type == SMDSAbs_Volume ||
4683            type < highType ||
4684            !elemSet.count(e))
4685         continue;
4686       if ( type > highType ) {
4687         nbInitElems = 0;
4688         highType    = type;
4689       }
4690       el = e;
4691       ++nbInitElems;
4692     }
4693     if ( nbInitElems == 1 ) {
4694       bool NotCreateEdge = el && el->IsMediumNode(node);
4695       if(!NotCreateEdge) {
4696         vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4697         list<const SMDS_MeshElement*> newEdges;
4698         sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4699       }
4700     }
4701   }
4702
4703   // Make a ceiling for each element ie an equal element of last new nodes.
4704   // Find free links of faces - make edges and sweep them into faces.
4705
4706   ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4707
4708   TTElemOfElemListMap::iterator  itElem      = newElemsMap.begin();
4709   TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4710   for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4711   {
4712     const SMDS_MeshElement* elem = itElem->first;
4713     vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4714
4715     if(itElem->second.size()==0) continue;
4716
4717     const bool isQuadratic = elem->IsQuadratic();
4718
4719     if ( elem->GetType() == SMDSAbs_Edge ) {
4720       // create a ceiling edge
4721       if ( !isQuadratic ) {
4722         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4723                                vecNewNodes[ 1 ]->second.back())) {
4724           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4725                                                       vecNewNodes[ 1 ]->second.back()));
4726           srcElements.push_back( elem );
4727         }
4728       }
4729       else {
4730         if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4731                                vecNewNodes[ 1 ]->second.back(),
4732                                vecNewNodes[ 2 ]->second.back())) {
4733           myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4734                                                       vecNewNodes[ 1 ]->second.back(),
4735                                                       vecNewNodes[ 2 ]->second.back()));
4736           srcElements.push_back( elem );
4737         }
4738       }
4739     }
4740     if ( elem->GetType() != SMDSAbs_Face )
4741       continue;
4742
4743     bool hasFreeLinks = false;
4744
4745     TIDSortedElemSet avoidSet;
4746     avoidSet.insert( elem );
4747
4748     set<const SMDS_MeshNode*> aFaceLastNodes;
4749     int iNode, nbNodes = vecNewNodes.size();
4750     if ( !isQuadratic ) {
4751       // loop on the face nodes
4752       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4753         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4754         // look for free links of the face
4755         int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4756         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4757         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4758         // check if a link n1-n2 is free
4759         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4760           hasFreeLinks = true;
4761           // make a new edge and a ceiling for a new edge
4762           const SMDS_MeshElement* edge;
4763           if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4764             myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4765             srcElements.push_back( myLastCreatedElems.back() );
4766           }
4767           n1 = vecNewNodes[ iNode ]->second.back();
4768           n2 = vecNewNodes[ iNext ]->second.back();
4769           if ( !aMesh->FindEdge( n1, n2 )) {
4770             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4771             srcElements.push_back( edge );
4772           }
4773         }
4774       }
4775     }
4776     else { // elem is quadratic face
4777       int nbn = nbNodes/2;
4778       for ( iNode = 0; iNode < nbn; iNode++ ) {
4779         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4780         int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4781         const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4782         const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4783         const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4784         // check if a link is free
4785         if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4786              ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4787              ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4788           hasFreeLinks = true;
4789           // make an edge and a ceiling for a new edge
4790           // find medium node
4791           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4792             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4793             srcElements.push_back( elem );
4794           }
4795           n1 = vecNewNodes[ iNode ]->second.back();
4796           n2 = vecNewNodes[ iNext ]->second.back();
4797           n3 = vecNewNodes[ iNode+nbn ]->second.back();
4798           if ( !aMesh->FindEdge( n1, n2, n3 )) {
4799             myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4800             srcElements.push_back( elem );
4801           }
4802         }
4803       }
4804       for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4805         aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4806       }
4807     }
4808
4809     // sweep free links into faces
4810
4811     if ( hasFreeLinks ) {
4812       list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4813       int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4814
4815       set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4816       set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4817       for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4818         initNodeSet.insert( vecNewNodes[ iNode ]->first );
4819         topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4820       }
4821       if ( isQuadratic && nbNodes % 2 ) {  // node set for the case of a biquadratic
4822         initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4823         initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4824       }
4825       for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4826         list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4827         std::advance( v, volNb );
4828         // find indices of free faces of a volume and their source edges
4829         list< int > freeInd;
4830         list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4831         SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4832         int iF, nbF = vTool.NbFaces();
4833         for ( iF = 0; iF < nbF; iF ++ ) {
4834           if (vTool.IsFreeFace( iF ) &&
4835               vTool.GetFaceNodes( iF, faceNodeSet ) &&
4836               initNodeSet != faceNodeSet) // except an initial face
4837           {
4838             if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4839               continue;
4840             if ( faceNodeSet == initNodeSetNoCenter )
4841               continue;
4842             freeInd.push_back( iF );
4843             // find source edge of a free face iF
4844             vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4845             vector<const SMDS_MeshNode*>::iterator lastCommom;
4846             commonNodes.resize( nbNodes, 0 );
4847             lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4848                                                 initNodeSet.begin(), initNodeSet.end(),
4849                                                 commonNodes.begin());
4850             if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4851               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4852             else
4853               srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4854 #ifdef _DEBUG_
4855             if ( !srcEdges.back() )
4856             {
4857               cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4858                    << iF << " of volume #" << vTool.ID() << endl;
4859             }
4860 #endif
4861           }
4862         }
4863         if ( freeInd.empty() )
4864           continue;
4865
4866         // create wall faces for all steps;
4867         // if such a face has been already created by sweep of edge,
4868         // assure that its orientation is OK
4869         for ( int iStep = 0; iStep < nbSteps; iStep++ )
4870         {
4871           vTool.Set( *v, /*ignoreCentralNodes=*/false );
4872           vTool.SetExternalNormal();
4873           const int nextShift = vTool.IsForward() ? +1 : -1;
4874           list< int >::iterator ind = freeInd.begin();
4875           list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4876           for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4877           {
4878             const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4879             int nbn = vTool.NbFaceNodes( *ind );
4880             const SMDS_MeshElement * f = 0;
4881             if ( nbn == 3 )              ///// triangle
4882             {
4883               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4884               if ( !f ||
4885                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4886               {
4887                 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4888                                                      nodes[ 1 ],
4889                                                      nodes[ 1 + nextShift ] };
4890                 if ( f )
4891                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4892                 else
4893                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4894                                                                newOrder[ 2 ] ));
4895               }
4896             }
4897             else if ( nbn == 4 )       ///// quadrangle
4898             {
4899               f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4900               if ( !f ||
4901                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4902               {
4903                 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4904                                                      nodes[ 2 ], nodes[ 2+nextShift ] };
4905                 if ( f )
4906                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4907                 else
4908                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4909                                                                newOrder[ 2 ], newOrder[ 3 ]));
4910               }
4911             }
4912             else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4913             {
4914               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4915               if ( !f ||
4916                    nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4917               {
4918                 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4919                                                      nodes[2],
4920                                                      nodes[2 + 2*nextShift],
4921                                                      nodes[3 - 2*nextShift],
4922                                                      nodes[3],
4923                                                      nodes[3 + 2*nextShift]};
4924                 if ( f )
4925                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4926                 else
4927                   myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4928                                                                newOrder[ 1 ],
4929                                                                newOrder[ 2 ],
4930                                                                newOrder[ 3 ],
4931                                                                newOrder[ 4 ],
4932                                                                newOrder[ 5 ] ));
4933               }
4934             }
4935             else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4936             {
4937               f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4938                                    nodes[1], nodes[3], nodes[5], nodes[7] );
4939               if ( !f ||
4940                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4941               {
4942                 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4943                                                      nodes[4 - 2*nextShift],
4944                                                      nodes[4],
4945                                                      nodes[4 + 2*nextShift],
4946                                                      nodes[1],
4947                                                      nodes[5 - 2*nextShift],
4948                                                      nodes[5],
4949                                                      nodes[5 + 2*nextShift] };
4950                 if ( f )
4951                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4952                 else
4953                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4954                                                               newOrder[ 2 ], newOrder[ 3 ],
4955                                                               newOrder[ 4 ], newOrder[ 5 ],
4956                                                               newOrder[ 6 ], newOrder[ 7 ]));
4957               }
4958             }
4959             else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4960             {
4961               f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4962                                       SMDSAbs_Face, /*noMedium=*/false);
4963               if ( !f ||
4964                    nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4965               {
4966                 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4967                                                      nodes[4 - 2*nextShift],
4968                                                      nodes[4],
4969                                                      nodes[4 + 2*nextShift],
4970                                                      nodes[1],
4971                                                      nodes[5 - 2*nextShift],
4972                                                      nodes[5],
4973                                                      nodes[5 + 2*nextShift],
4974                                                      nodes[8] };
4975                 if ( f )
4976                   aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4977                 else
4978                   myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4979                                                               newOrder[ 2 ], newOrder[ 3 ],
4980                                                               newOrder[ 4 ], newOrder[ 5 ],
4981                                                               newOrder[ 6 ], newOrder[ 7 ],
4982                                                               newOrder[ 8 ]));
4983               }
4984             }
4985             else  //////// polygon
4986             {
4987               vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4988               const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4989               if ( !f ||
4990                    nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4991               {
4992                 if ( !vTool.IsForward() )
4993                   std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4994                 if ( f )
4995                   aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4996                 else
4997                   AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4998               }
4999             }
5000
5001             while ( srcElements.size() < myLastCreatedElems.size() )
5002               srcElements.push_back( *srcEdge );
5003
5004           }  // loop on free faces
5005
5006           // go to the next volume
5007           iVol = 0;
5008           while ( iVol++ < nbVolumesByStep ) v++;
5009
5010         } // loop on steps
5011       } // loop on volumes of one step
5012     } // sweep free links into faces
5013
5014     // Make a ceiling face with a normal external to a volume
5015
5016     // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5017     SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5018     int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5019
5020     if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5021       aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5022       iF = lastVol.GetFaceIndex( aFaceLastNodes );
5023     }
5024     if ( iF >= 0 )
5025     {
5026       lastVol.SetExternalNormal();
5027       const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5028       const               int nbn = lastVol.NbFaceNodes( iF );
5029       vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5030       if ( !hasFreeLinks ||
5031            !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5032       {
5033         const vector<int>& interlace =
5034           SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5035         SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5036
5037         AddElement( nodeVec, anyFace.Init( elem ));
5038
5039         while ( srcElements.size() < myLastCreatedElems.size() )
5040           srcElements.push_back( elem );
5041       }
5042     }
5043   } // loop on swept elements
5044 }
5045
5046 //=======================================================================
5047 //function : RotationSweep
5048 //purpose  :
5049 //=======================================================================
5050
5051 SMESH_MeshEditor::PGroupIDs
5052 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet   theElemSets[2],
5053                                 const gp_Ax1&      theAxis,
5054                                 const double       theAngle,
5055                                 const int          theNbSteps,
5056                                 const double       theTol,
5057                                 const bool         theMakeGroups,
5058                                 const bool         theMakeWalls)
5059 {
5060   ClearLastCreated();
5061
5062   setElemsFirst( theElemSets );
5063   myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5064   myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5065
5066   // source elements for each generated one
5067   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5068   srcElems.reserve( theElemSets[0].size() );
5069   srcNodes.reserve( theElemSets[1].size() );
5070
5071   gp_Trsf aTrsf;
5072   aTrsf.SetRotation( theAxis, theAngle );
5073   gp_Trsf aTrsf2;
5074   aTrsf2.SetRotation( theAxis, theAngle/2. );
5075
5076   gp_Lin aLine( theAxis );
5077   double aSqTol = theTol * theTol;
5078
5079   SMESHDS_Mesh* aMesh = GetMeshDS();
5080
5081   TNodeOfNodeListMap mapNewNodes;
5082   TElemOfVecOfNnlmiMap mapElemNewNodes;
5083   TTElemOfElemListMap newElemsMap;
5084
5085   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5086                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5087                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5088   // loop on theElemSets
5089   TIDSortedElemSet::iterator itElem;
5090   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5091   {
5092     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5093     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5094       const SMDS_MeshElement* elem = *itElem;
5095       if ( !elem || elem->GetType() == SMDSAbs_Volume )
5096         continue;
5097       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5098       newNodesItVec.reserve( elem->NbNodes() );
5099
5100       // loop on elem nodes
5101       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5102       while ( itN->more() )
5103       {
5104         const SMDS_MeshNode* node = cast2Node( itN->next() );
5105
5106         gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5107         double coord[3];
5108         aXYZ.Coord( coord[0], coord[1], coord[2] );
5109         bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5110
5111         // check if a node has been already sweeped
5112         TNodeOfNodeListMapItr nIt =
5113           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5114         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5115         if ( listNewNodes.empty() )
5116         {
5117           // check if we are to create medium nodes between corner ones
5118           bool needMediumNodes = false;
5119           if ( isQuadraticMesh )
5120           {
5121             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5122             while (it->more() && !needMediumNodes )
5123             {
5124               const SMDS_MeshElement* invElem = it->next();
5125               if ( invElem != elem && !theElems.count( invElem )) continue;
5126               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5127               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5128                 needMediumNodes = true;
5129             }
5130           }
5131
5132           // make new nodes
5133           const SMDS_MeshNode * newNode = node;
5134           for ( int i = 0; i < theNbSteps; i++ ) {
5135             if ( !isOnAxis ) {
5136               if ( needMediumNodes )  // create a medium node
5137               {
5138                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5139                 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5140                 myLastCreatedNodes.push_back(newNode);
5141                 srcNodes.push_back( node );
5142                 listNewNodes.push_back( newNode );
5143                 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5144               }
5145               else {
5146                 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5147               }
5148               // create a corner node
5149               newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5150               myLastCreatedNodes.push_back(newNode);
5151               srcNodes.push_back( node );
5152               listNewNodes.push_back( newNode );
5153             }
5154             else {
5155               listNewNodes.push_back( newNode );
5156               // if ( needMediumNodes )
5157               //   listNewNodes.push_back( newNode );
5158             }
5159           }
5160         }
5161         newNodesItVec.push_back( nIt );
5162       }
5163       // make new elements
5164       sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5165     }
5166   }
5167
5168   if ( theMakeWalls )
5169     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5170
5171   PGroupIDs newGroupIDs;
5172   if ( theMakeGroups )
5173     newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5174
5175   return newGroupIDs;
5176 }
5177
5178 //=======================================================================
5179 //function : ExtrusParam
5180 //purpose  : standard construction
5181 //=======================================================================
5182
5183 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec&            theStep,
5184                                             const int                theNbSteps,
5185                                             const std::list<double>& theScales,
5186                                             const gp_XYZ*            theBasePoint,
5187                                             const int                theFlags,
5188                                             const double             theTolerance):
5189   myDir( theStep ),
5190   myBaseP( Precision::Infinite(), 0, 0 ),
5191   myFlags( theFlags ),
5192   myTolerance( theTolerance ),
5193   myElemsToUse( NULL )
5194 {
5195   mySteps = new TColStd_HSequenceOfReal;
5196   const double stepSize = theStep.Magnitude();
5197   for (int i=1; i<=theNbSteps; i++ )
5198     mySteps->Append( stepSize );
5199
5200   int nbScales = theScales.size();
5201   if ( nbScales > 0 )
5202   {
5203     if ( IsLinearVariation() && nbScales < theNbSteps )
5204     {
5205       myScales.reserve( theNbSteps );
5206       std::list<double>::const_iterator scale = theScales.begin();
5207       double prevScale = 1.0;
5208       for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5209       {
5210         int      iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5211         int    stDelta = Max( 1, iStep - myScales.size());
5212         double scDelta = ( *scale - prevScale ) / stDelta;
5213         for ( int iStep = 0; iStep < stDelta; ++iStep )
5214         {
5215           myScales.push_back( prevScale + scDelta );
5216           prevScale = myScales.back();
5217         }
5218         prevScale = *scale;
5219       }
5220     }
5221     else
5222     {
5223       myScales.assign( theScales.begin(), theScales.end() );
5224     }
5225   }
5226   if ( theBasePoint )
5227   {
5228     myBaseP = *theBasePoint;
5229   }
5230
5231   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5232       ( theTolerance > 0 ))
5233   {
5234     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5235   }
5236   else
5237   {
5238     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5239   }
5240 }
5241
5242 //=======================================================================
5243 //function : ExtrusParam
5244 //purpose  : steps are given explicitly
5245 //=======================================================================
5246
5247 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir&                   theDir,
5248                                             Handle(TColStd_HSequenceOfReal) theSteps,
5249                                             const int                       theFlags,
5250                                             const double                    theTolerance):
5251   myDir( theDir ),
5252   mySteps( theSteps ),
5253   myFlags( theFlags ),
5254   myTolerance( theTolerance ),
5255   myElemsToUse( NULL )
5256 {
5257   if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5258       ( theTolerance > 0 ))
5259   {
5260     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5261   }
5262   else
5263   {
5264     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5265   }
5266 }
5267
5268 //=======================================================================
5269 //function : ExtrusParam
5270 //purpose  : for extrusion by normal
5271 //=======================================================================
5272
5273 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5274                                             const int    theNbSteps,
5275                                             const int    theFlags,
5276                                             const int    theDim ):
5277   myDir( 1,0,0 ),
5278   mySteps( new TColStd_HSequenceOfReal ),
5279   myFlags( theFlags ),
5280   myTolerance( 0 ),
5281   myElemsToUse( NULL )
5282 {
5283   for (int i = 0; i < theNbSteps; i++ )
5284     mySteps->Append( theStepSize );
5285
5286   if ( theDim == 1 )
5287   {
5288     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5289   }
5290   else
5291   {
5292     myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5293   }
5294 }
5295
5296 //=======================================================================
5297 //function : ExtrusParam::SetElementsToUse
5298 //purpose  : stores elements to use for extrusion by normal, depending on
5299 //           state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5300 //           define myBaseP for scaling
5301 //=======================================================================
5302
5303 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5304                                                       const TIDSortedElemSet& nodes )
5305 {
5306   myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5307
5308   if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5309   {
5310     myBaseP.SetCoord( 0.,0.,0. );
5311     TIDSortedElemSet newNodes;
5312
5313     const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5314     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5315     {
5316       const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5317       TIDSortedElemSet::const_iterator itElem = elements.begin();
5318       for ( ; itElem != elements.end(); itElem++ )
5319       {
5320         const SMDS_MeshElement* elem = *itElem;
5321         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
5322         while ( itN->more() ) {
5323           const SMDS_MeshElement* node = itN->next();
5324           if ( newNodes.insert( node ).second )
5325             myBaseP += SMESH_TNodeXYZ( node );
5326         }
5327       }
5328     }
5329     myBaseP /= newNodes.size();
5330   }
5331 }
5332
5333 //=======================================================================
5334 //function : ExtrusParam::beginStepIter
5335 //purpose  : prepare iteration on steps
5336 //=======================================================================
5337
5338 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5339 {
5340   myWithMediumNodes = withMediumNodes;
5341   myNextStep = 1;
5342   myCurSteps.clear();
5343 }
5344 //=======================================================================
5345 //function : ExtrusParam::moreSteps
5346 //purpose  : are there more steps?
5347 //=======================================================================
5348
5349 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5350 {
5351   return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5352 }
5353 //=======================================================================
5354 //function : ExtrusParam::nextStep
5355 //purpose  : returns the next step
5356 //=======================================================================
5357
5358 double SMESH_MeshEditor::ExtrusParam::nextStep()
5359 {
5360   double res = 0;
5361   if ( !myCurSteps.empty() )
5362   {
5363     res = myCurSteps.back();
5364     myCurSteps.pop_back();
5365   }
5366   else if ( myNextStep <= mySteps->Length() )
5367   {
5368     myCurSteps.push_back( mySteps->Value( myNextStep ));
5369     ++myNextStep;
5370     if ( myWithMediumNodes )
5371     {
5372       myCurSteps.back() /= 2.;
5373       myCurSteps.push_back( myCurSteps.back() );
5374     }
5375     res = nextStep();
5376   }
5377   return res;
5378 }
5379
5380 //=======================================================================
5381 //function : ExtrusParam::makeNodesByDir
5382 //purpose  : create nodes for standard extrusion
5383 //=======================================================================
5384
5385 int SMESH_MeshEditor::ExtrusParam::
5386 makeNodesByDir( SMESHDS_Mesh*                     mesh,
5387                 const SMDS_MeshNode*              srcNode,
5388                 std::list<const SMDS_MeshNode*> & newNodes,
5389                 const bool                        makeMediumNodes)
5390 {
5391   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5392
5393   int nbNodes = 0;
5394   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5395   {
5396     p += myDir.XYZ() * nextStep();
5397     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5398     newNodes.push_back( newNode );
5399   }
5400
5401   if ( !myScales.empty() )
5402   {
5403     if ( makeMediumNodes && myMediumScales.empty() )
5404     {
5405       myMediumScales.resize( myScales.size() );
5406       double prevFactor = 1.;
5407       for ( size_t i = 0; i < myScales.size(); ++i )
5408       {
5409         myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5410         prevFactor = myScales[i];
5411       }
5412     }
5413     typedef std::vector<double>::iterator ScaleIt;
5414     ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5415
5416     size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5417
5418     gp_XYZ center = myBaseP;
5419     std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5420     size_t iN  = 0;
5421     for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5422     {
5423       center += myDir.XYZ() * nextStep();
5424
5425       iSc += int( makeMediumNodes );
5426       ScaleIt& scale = scales[ iSc % 2 ];
5427       
5428       gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5429       xyz = ( *scale * ( xyz - center )) + center;
5430       mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5431
5432       ++scale;
5433     }
5434   }
5435   return nbNodes;
5436 }
5437
5438 //=======================================================================
5439 //function : ExtrusParam::makeNodesByDirAndSew
5440 //purpose  : create nodes for standard extrusion with sewing
5441 //=======================================================================
5442
5443 int SMESH_MeshEditor::ExtrusParam::
5444 makeNodesByDirAndSew( SMESHDS_Mesh*                     mesh,
5445                       const SMDS_MeshNode*              srcNode,
5446                       std::list<const SMDS_MeshNode*> & newNodes,
5447                       const bool                        makeMediumNodes)
5448 {
5449   gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5450
5451   int nbNodes = 0;
5452   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5453   {
5454     P1 += myDir.XYZ() * nextStep();
5455
5456     // try to search in sequence of existing nodes
5457     // if myNodes.size()>0 we 'nave to use given sequence
5458     // else - use all nodes of mesh
5459     const SMDS_MeshNode * node = 0;
5460     if ( myNodes.Length() > 0 ) {
5461       int i;
5462       for ( i = 1; i <= myNodes.Length(); i++ ) {
5463         gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5464         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5465         {
5466           node = myNodes.Value(i);
5467           break;
5468         }
5469       }
5470     }
5471     else {
5472       SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5473       while(itn->more()) {
5474         SMESH_TNodeXYZ P2( itn->next() );
5475         if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5476         {
5477           node = P2._node;
5478           break;
5479         }
5480       }
5481     }
5482
5483     if ( !node )
5484       node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5485
5486     newNodes.push_back( node );
5487
5488   } // loop on steps
5489
5490   return nbNodes;
5491 }
5492
5493 //=======================================================================
5494 //function : ExtrusParam::makeNodesByNormal2D
5495 //purpose  : create nodes for extrusion using normals of faces
5496 //=======================================================================
5497
5498 int SMESH_MeshEditor::ExtrusParam::
5499 makeNodesByNormal2D( SMESHDS_Mesh*                     mesh,
5500                      const SMDS_MeshNode*              srcNode,
5501                      std::list<const SMDS_MeshNode*> & newNodes,
5502                      const bool                        makeMediumNodes)
5503 {
5504   const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5505
5506   gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5507
5508   // get normals to faces sharing srcNode
5509   vector< gp_XYZ > norms, baryCenters;
5510   gp_XYZ norm, avgNorm( 0,0,0 );
5511   SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5512   while ( faceIt->more() )
5513   {
5514     const SMDS_MeshElement* face = faceIt->next();
5515     if ( myElemsToUse && !myElemsToUse->count( face ))
5516       continue;
5517     if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5518     {
5519       norms.push_back( norm );
5520       avgNorm += norm;
5521       if ( !alongAvgNorm )
5522       {
5523         gp_XYZ bc(0,0,0);
5524         int nbN = 0;
5525         for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5526           bc += SMESH_TNodeXYZ( nIt->next() );
5527         baryCenters.push_back( bc / nbN );
5528       }
5529     }
5530   }
5531
5532   if ( norms.empty() ) return 0;
5533
5534   double normSize = avgNorm.Modulus();
5535   if ( normSize < std::numeric_limits<double>::min() )
5536     return 0;
5537
5538   if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5539   {
5540     myDir = avgNorm;
5541     return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5542   }
5543
5544   avgNorm /= normSize;
5545
5546   int nbNodes = 0;
5547   for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5548   {
5549     gp_XYZ pNew = p;
5550     double stepSize = nextStep();
5551
5552     if ( norms.size() > 1 )
5553     {
5554       for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5555       {
5556         // translate plane of a face
5557         baryCenters[ iF ] += norms[ iF ] * stepSize;
5558
5559         // find point of intersection of the face plane located at baryCenters[ iF ]
5560         // and avgNorm located at pNew
5561         double d    = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5562         double dot  = ( norms[ iF ] * avgNorm );
5563         if ( dot < std::numeric_limits<double>::min() )
5564           dot = stepSize * 1e-3;
5565         double step = -( norms[ iF ] * pNew + d ) / dot;
5566         pNew += step * avgNorm;
5567       }
5568     }
5569     else
5570     {
5571       pNew += stepSize * avgNorm;
5572     }
5573     p = pNew;
5574
5575     const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5576     newNodes.push_back( newNode );
5577   }
5578   return nbNodes;
5579 }
5580
5581 //=======================================================================
5582 //function : ExtrusParam::makeNodesByNormal1D
5583 //purpose  : create nodes for extrusion using normals of edges
5584 //=======================================================================
5585
5586 int SMESH_MeshEditor::ExtrusParam::
5587 makeNodesByNormal1D( SMESHDS_Mesh*                     mesh,
5588                      const SMDS_MeshNode*              srcNode,
5589                      std::list<const SMDS_MeshNode*> & newNodes,
5590                      const bool                        makeMediumNodes)
5591 {
5592   throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5593   return 0;
5594 }
5595
5596 //=======================================================================
5597 //function : ExtrusionSweep
5598 //purpose  :
5599 //=======================================================================
5600
5601 SMESH_MeshEditor::PGroupIDs
5602 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElems[2],
5603                                   const gp_Vec&        theStep,
5604                                   const int            theNbSteps,
5605                                   TTElemOfElemListMap& newElemsMap,
5606                                   const int            theFlags,
5607                                   const double         theTolerance)
5608 {
5609   ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5610   return ExtrusionSweep( theElems, aParams, newElemsMap );
5611 }
5612
5613
5614 //=======================================================================
5615 //function : ExtrusionSweep
5616 //purpose  :
5617 //=======================================================================
5618
5619 SMESH_MeshEditor::PGroupIDs
5620 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet     theElemSets[2],
5621                                   ExtrusParam&         theParams,
5622                                   TTElemOfElemListMap& newElemsMap)
5623 {
5624   ClearLastCreated();
5625
5626   setElemsFirst( theElemSets );
5627   myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5628   myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5629
5630   // source elements for each generated one
5631   SMESH_SequenceOfElemPtr srcElems, srcNodes;
5632   srcElems.reserve( theElemSets[0].size() );
5633   srcNodes.reserve( theElemSets[1].size() );
5634
5635   const int nbSteps = theParams.NbSteps();
5636   theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5637
5638   TNodeOfNodeListMap   mapNewNodes;
5639   TElemOfVecOfNnlmiMap mapElemNewNodes;
5640
5641   const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5642                                      myMesh->NbFaces(ORDER_QUADRATIC) +
5643                                      myMesh->NbVolumes(ORDER_QUADRATIC) );
5644   // loop on theElems
5645   TIDSortedElemSet::iterator itElem;
5646   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5647   {
5648     TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5649     for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5650     {
5651       // check element type
5652       const SMDS_MeshElement* elem = *itElem;
5653       if ( !elem  || elem->GetType() == SMDSAbs_Volume )
5654         continue;
5655
5656       const size_t nbNodes = elem->NbNodes();
5657       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5658       newNodesItVec.reserve( nbNodes );
5659
5660       // loop on elem nodes
5661       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5662       while ( itN->more() )
5663       {
5664         // check if a node has been already sweeped
5665         const SMDS_MeshNode* node = cast2Node( itN->next() );
5666         TNodeOfNodeListMap::iterator nIt =
5667           mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5668         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5669         if ( listNewNodes.empty() )
5670         {
5671           // make new nodes
5672
5673           // check if we are to create medium nodes between corner ones
5674           bool needMediumNodes = false;
5675           if ( isQuadraticMesh )
5676           {
5677             SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5678             while (it->more() && !needMediumNodes )
5679             {
5680               const SMDS_MeshElement* invElem = it->next();
5681               if ( invElem != elem && !theElems.count( invElem )) continue;
5682               needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5683               if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5684                 needMediumNodes = true;
5685             }
5686           }
5687           // create nodes for all steps
5688           if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5689           {
5690             list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5691             for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5692             {
5693               myLastCreatedNodes.push_back( *newNodesIt );
5694               srcNodes.push_back( node );
5695             }
5696           }
5697           else
5698           {
5699             break; // newNodesItVec will be shorter than nbNodes
5700           }
5701         }
5702         newNodesItVec.push_back( nIt );
5703       }
5704       // make new elements
5705       if ( newNodesItVec.size() == nbNodes )
5706         sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5707     }
5708   }
5709
5710   if ( theParams.ToMakeBoundary() ) {
5711     makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5712   }
5713   PGroupIDs newGroupIDs;
5714   if ( theParams.ToMakeGroups() )
5715     newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5716
5717   return newGroupIDs;
5718 }
5719
5720 //=======================================================================
5721 //function : ExtrusionAlongTrack
5722 //purpose  :
5723 //=======================================================================
5724 SMESH_MeshEditor::Extrusion_Error
5725 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5726                                        SMESH_subMesh*       theTrack,
5727                                        const SMDS_MeshNode* theN1,
5728                                        const bool           theHasAngles,
5729                                        list<double>&        theAngles,
5730                                        const bool           theLinearVariation,
5731                                        const bool           theHasRefPoint,
5732                                        const gp_Pnt&        theRefPoint,
5733                                        const bool           theMakeGroups)
5734 {
5735   ClearLastCreated();
5736
5737   int aNbE;
5738   std::list<double> aPrms;
5739   TIDSortedElemSet::iterator itElem;
5740
5741   gp_XYZ aGC;
5742   TopoDS_Edge aTrackEdge;
5743   TopoDS_Vertex aV1, aV2;
5744
5745   SMDS_ElemIteratorPtr aItE;
5746   SMDS_NodeIteratorPtr aItN;
5747   SMDSAbs_ElementType aTypeE;
5748
5749   TNodeOfNodeListMap mapNewNodes;
5750
5751   // 1. Check data
5752   aNbE = theElements[0].size() + theElements[1].size();
5753   // nothing to do
5754   if ( !aNbE )
5755     return EXTR_NO_ELEMENTS;
5756
5757   // 1.1 Track Pattern
5758   ASSERT( theTrack );
5759
5760   SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5761   if ( !pSubMeshDS )
5762     return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5763                                 theHasAngles, theAngles, theLinearVariation,
5764                                 theHasRefPoint, theRefPoint, theMakeGroups );
5765
5766   aItE = pSubMeshDS->GetElements();
5767   while ( aItE->more() ) {
5768     const SMDS_MeshElement* pE = aItE->next();
5769     aTypeE = pE->GetType();
5770     // Pattern must contain links only
5771     if ( aTypeE != SMDSAbs_Edge )
5772       return EXTR_PATH_NOT_EDGE;
5773   }
5774
5775   list<SMESH_MeshEditor_PathPoint> fullList;
5776
5777   const TopoDS_Shape& aS = theTrack->GetSubShape();
5778   // Sub-shape for the Pattern must be an Edge or Wire
5779   if( aS.ShapeType() == TopAbs_EDGE ) {
5780     aTrackEdge = TopoDS::Edge( aS );
5781     // the Edge must not be degenerated
5782     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5783       return EXTR_BAD_PATH_SHAPE;
5784     TopExp::Vertices( aTrackEdge, aV1, aV2 );
5785     aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5786     const SMDS_MeshNode* aN1 = aItN->next();
5787     aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5788     const SMDS_MeshNode* aN2 = aItN->next();
5789     // starting node must be aN1 or aN2
5790     if ( !( aN1 == theN1 || aN2 == theN1 ) )
5791       return EXTR_BAD_STARTING_NODE;
5792     aItN = pSubMeshDS->GetNodes();
5793     while ( aItN->more() ) {
5794       const SMDS_MeshNode* pNode = aItN->next();
5795       const SMDS_EdgePosition* pEPos =
5796         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5797       double aT = pEPos->GetUParameter();
5798       aPrms.push_back( aT );
5799     }
5800     //Extrusion_Error err =
5801     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5802   } else if( aS.ShapeType() == TopAbs_WIRE ) {
5803     list< SMESH_subMesh* > LSM;
5804     TopTools_SequenceOfShape Edges;
5805     SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5806     while(itSM->more()) {
5807       SMESH_subMesh* SM = itSM->next();
5808       LSM.push_back(SM);
5809       const TopoDS_Shape& aS = SM->GetSubShape();
5810       Edges.Append(aS);
5811     }
5812     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5813     int startNid = theN1->GetID();
5814     TColStd_MapOfInteger UsedNums;
5815
5816     int NbEdges = Edges.Length();
5817     int i = 1;
5818     for(; i<=NbEdges; i++) {
5819       int k = 0;
5820       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5821       for(; itLSM!=LSM.end(); itLSM++) {
5822         k++;
5823         if(UsedNums.Contains(k)) continue;
5824         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5825         SMESH_subMesh* locTrack = *itLSM;
5826         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5827         TopExp::Vertices( aTrackEdge, aV1, aV2 );
5828         aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5829         const SMDS_MeshNode* aN1 = aItN->next();
5830         aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5831         const SMDS_MeshNode* aN2 = aItN->next();
5832         // starting node must be aN1 or aN2
5833         if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5834         // 2. Collect parameters on the track edge
5835         aPrms.clear();
5836         aItN = locMeshDS->GetNodes();
5837         while ( aItN->more() ) {
5838           const SMDS_MeshNode* pNode = aItN->next();
5839           const SMDS_EdgePosition* pEPos =
5840             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5841           double aT = pEPos->GetUParameter();
5842           aPrms.push_back( aT );
5843         }
5844         list<SMESH_MeshEditor_PathPoint> LPP;
5845         //Extrusion_Error err =
5846         MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5847         LLPPs.push_back(LPP);
5848         UsedNums.Add(k);
5849         // update startN for search following egde
5850         if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5851         else startNid = aN1->GetID();
5852         break;
5853       }
5854     }
5855     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5856     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5857     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5858     for(; itPP!=firstList.end(); itPP++) {
5859       fullList.push_back( *itPP );
5860     }
5861     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5862     fullList.pop_back();
5863     itLLPP++;
5864     for(; itLLPP!=LLPPs.end(); itLLPP++) {
5865       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5866       itPP = currList.begin();
5867       SMESH_MeshEditor_PathPoint PP2 = currList.front();
5868       gp_Dir D1 = PP1.Tangent();
5869       gp_Dir D2 = PP2.Tangent();
5870       gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5871                            (D1.Z()+D2.Z())/2 ) );
5872       PP1.SetTangent(Dnew);
5873       fullList.push_back(PP1);
5874       itPP++;
5875       for(; itPP!=firstList.end(); itPP++) {
5876         fullList.push_back( *itPP );
5877       }
5878       PP1 = fullList.back();
5879       fullList.pop_back();
5880     }
5881     // if wire not closed
5882     fullList.push_back(PP1);
5883     // else ???
5884   }
5885   else {
5886     return EXTR_BAD_PATH_SHAPE;
5887   }
5888
5889   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5890                           theHasRefPoint, theRefPoint, theMakeGroups);
5891 }
5892
5893
5894 //=======================================================================
5895 //function : ExtrusionAlongTrack
5896 //purpose  :
5897 //=======================================================================
5898 SMESH_MeshEditor::Extrusion_Error
5899 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet     theElements[2],
5900                                        SMESH_Mesh*          theTrack,
5901                                        const SMDS_MeshNode* theN1,
5902                                        const bool           theHasAngles,
5903                                        list<double>&        theAngles,
5904                                        const bool           theLinearVariation,
5905                                        const bool           theHasRefPoint,
5906                                        const gp_Pnt&        theRefPoint,
5907                                        const bool           theMakeGroups)
5908 {
5909   ClearLastCreated();
5910
5911   int aNbE;
5912   std::list<double> aPrms;
5913   TIDSortedElemSet::iterator itElem;
5914
5915   gp_XYZ aGC;
5916   TopoDS_Edge aTrackEdge;
5917   TopoDS_Vertex aV1, aV2;
5918
5919   SMDS_ElemIteratorPtr aItE;
5920   SMDS_NodeIteratorPtr aItN;
5921   SMDSAbs_ElementType aTypeE;
5922
5923   TNodeOfNodeListMap mapNewNodes;
5924
5925   // 1. Check data
5926   aNbE = theElements[0].size() + theElements[1].size();
5927   // nothing to do
5928   if ( !aNbE )
5929     return EXTR_NO_ELEMENTS;
5930
5931   // 1.1 Track Pattern
5932   ASSERT( theTrack );
5933
5934   SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5935
5936   aItE = pMeshDS->elementsIterator();
5937   while ( aItE->more() ) {
5938     const SMDS_MeshElement* pE = aItE->next();
5939     aTypeE = pE->GetType();
5940     // Pattern must contain links only
5941     if ( aTypeE != SMDSAbs_Edge )
5942       return EXTR_PATH_NOT_EDGE;
5943   }
5944
5945   list<SMESH_MeshEditor_PathPoint> fullList;
5946
5947   const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5948
5949   if ( !theTrack->HasShapeToMesh() ) {
5950     //Mesh without shape
5951     const SMDS_MeshNode* currentNode = NULL;
5952     const SMDS_MeshNode* prevNode = theN1;
5953     std::vector<const SMDS_MeshNode*> aNodesList;
5954     aNodesList.push_back(theN1);
5955     int nbEdges = 0, conn=0;
5956     const SMDS_MeshElement* prevElem = NULL;
5957     const SMDS_MeshElement* currentElem = NULL;
5958     int totalNbEdges = theTrack->NbEdges();
5959     SMDS_ElemIteratorPtr nIt;
5960
5961     //check start node
5962     if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5963       return EXTR_BAD_STARTING_NODE;
5964     }
5965
5966     conn = nbEdgeConnectivity(theN1);
5967     if( conn != 1 )
5968       return EXTR_PATH_NOT_EDGE;
5969
5970     aItE = theN1->GetInverseElementIterator();
5971     prevElem = aItE->next();
5972     currentElem = prevElem;
5973     //Get all nodes
5974     if(totalNbEdges == 1 ) {
5975       nIt = currentElem->nodesIterator();
5976       currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5977       if(currentNode == prevNode)
5978         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5979       aNodesList.push_back(currentNode);
5980     } else {
5981       nIt = currentElem->nodesIterator();
5982       while( nIt->more() ) {
5983         currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5984         if(currentNode == prevNode)
5985           currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5986         aNodesList.push_back(currentNode);
5987
5988         //case of the closed mesh
5989         if(currentNode == theN1) {
5990           nbEdges++;
5991           break;
5992         }
5993
5994         conn = nbEdgeConnectivity(currentNode);
5995         if(conn > 2) {
5996           return EXTR_PATH_NOT_EDGE;
5997         }else if( conn == 1 && nbEdges > 0 ) {
5998           //End of the path
5999           nbEdges++;
6000           break;
6001         }else {
6002           prevNode = currentNode;
6003           aItE = currentNode->GetInverseElementIterator();
6004           currentElem = aItE->next();
6005           if( currentElem  == prevElem)
6006             currentElem = aItE->next();
6007           nIt = currentElem->nodesIterator();
6008           prevElem = currentElem;
6009           nbEdges++;
6010         }
6011       }
6012     }
6013
6014     if(nbEdges != totalNbEdges)
6015       return EXTR_PATH_NOT_EDGE;
6016
6017     TopTools_SequenceOfShape Edges;
6018     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6019     int startNid = theN1->GetID();
6020     for ( size_t i = 1; i < aNodesList.size(); i++ )
6021     {
6022       gp_Pnt     p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6023       gp_Pnt     p2 = SMESH_TNodeXYZ( aNodesList[i] );
6024       TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6025       list<SMESH_MeshEditor_PathPoint> LPP;
6026       aPrms.clear();
6027       MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6028       LLPPs.push_back(LPP);
6029       if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i  ]->GetID();
6030       else                                        startNid = aNodesList[i-1]->GetID();
6031     }
6032
6033     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6034     list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6035     list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6036     for(; itPP!=firstList.end(); itPP++) {
6037       fullList.push_back( *itPP );
6038     }
6039
6040     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6041     SMESH_MeshEditor_PathPoint PP2;
6042     fullList.pop_back();
6043     itLLPP++;
6044     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6045       list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6046       itPP = currList.begin();
6047       PP2 = currList.front();
6048       gp_Dir D1 = PP1.Tangent();
6049       gp_Dir D2 = PP2.Tangent();
6050       gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6051       PP1.SetTangent(Dnew);
6052       fullList.push_back(PP1);
6053       itPP++;
6054       for(; itPP!=currList.end(); itPP++) {
6055         fullList.push_back( *itPP );
6056       }
6057       PP1 = fullList.back();
6058       fullList.pop_back();
6059     }
6060     fullList.push_back(PP1);
6061
6062   } // Sub-shape for the Pattern must be an Edge or Wire
6063   else if ( aS.ShapeType() == TopAbs_EDGE )
6064   {
6065     aTrackEdge = TopoDS::Edge( aS );
6066     // the Edge must not be degenerated
6067     if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6068       return EXTR_BAD_PATH_SHAPE;
6069     TopExp::Vertices( aTrackEdge, aV1, aV2 );
6070     const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6071     const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6072     // starting node must be aN1 or aN2
6073     if ( !( aN1 == theN1 || aN2 == theN1 ) )
6074       return EXTR_BAD_STARTING_NODE;
6075     aItN = pMeshDS->nodesIterator();
6076     while ( aItN->more() ) {
6077       const SMDS_MeshNode* pNode = aItN->next();
6078       if( pNode==aN1 || pNode==aN2 ) continue;
6079       const SMDS_EdgePosition* pEPos =
6080         static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6081       double aT = pEPos->GetUParameter();
6082       aPrms.push_back( aT );
6083     }
6084     //Extrusion_Error err =
6085     MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6086   }
6087   else if( aS.ShapeType() == TopAbs_WIRE ) {
6088     list< SMESH_subMesh* > LSM;
6089     TopTools_SequenceOfShape Edges;
6090     TopExp_Explorer eExp(aS, TopAbs_EDGE);
6091     for(; eExp.More(); eExp.Next()) {
6092       TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6093       if( SMESH_Algo::isDegenerated(E) ) continue;
6094       SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6095       if(SM) {
6096         LSM.push_back(SM);
6097         Edges.Append(E);
6098       }
6099     }
6100     list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6101     TopoDS_Vertex aVprev;
6102     TColStd_MapOfInteger UsedNums;
6103     int NbEdges = Edges.Length();
6104     int i = 1;
6105     for(; i<=NbEdges; i++) {
6106       int k = 0;
6107       list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6108       for(; itLSM!=LSM.end(); itLSM++) {
6109         k++;
6110         if(UsedNums.Contains(k)) continue;
6111         aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6112         SMESH_subMesh* locTrack = *itLSM;
6113         SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6114         TopExp::Vertices( aTrackEdge, aV1, aV2 );
6115         bool aN1isOK = false, aN2isOK = false;
6116         if ( aVprev.IsNull() ) {
6117           // if previous vertex is not yet defined, it means that we in the beginning of wire
6118           // and we have to find initial vertex corresponding to starting node theN1
6119           const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6120           const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6121           // starting node must be aN1 or aN2
6122           aN1isOK = ( aN1 && aN1 == theN1 );
6123           aN2isOK = ( aN2 && aN2 == theN1 );
6124         }
6125         else {
6126           // we have specified ending vertex of the previous edge on the previous iteration
6127           // and we have just to check that it corresponds to any vertex in current segment
6128           aN1isOK = aVprev.IsSame( aV1 );
6129           aN2isOK = aVprev.IsSame( aV2 );
6130         }
6131         if ( !aN1isOK && !aN2isOK ) continue;
6132         // 2. Collect parameters on the track edge
6133         aPrms.clear();
6134         aItN = locMeshDS->GetNodes();
6135         while ( aItN->more() ) {
6136           const SMDS_MeshNode*     pNode = aItN->next();
6137           const SMDS_EdgePosition* pEPos =
6138             static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6139           double aT = pEPos->GetUParameter();
6140           aPrms.push_back( aT );
6141         }
6142         list<SMESH_MeshEditor_PathPoint> LPP;
6143         //Extrusion_Error err =
6144         MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6145         LLPPs.push_back(LPP);
6146         UsedNums.Add(k);
6147         // update startN for search following egde
6148         if ( aN1isOK ) aVprev = aV2;
6149         else           aVprev = aV1;
6150         break;
6151       }
6152     }
6153     list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154     list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6155     fullList.splice( fullList.end(), firstList );
6156
6157     SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158     fullList.pop_back();
6159     itLLPP++;
6160     for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161       list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6162       SMESH_MeshEditor_PathPoint PP2 = currList.front();
6163       gp_Dir D1 = PP1.Tangent();
6164       gp_Dir D2 = PP2.Tangent();
6165       gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6166       PP1.SetTangent(Dnew);
6167       fullList.push_back(PP1);
6168       fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6169       PP1 = fullList.back();
6170       fullList.pop_back();
6171     }
6172     // if wire not closed
6173     fullList.push_back(PP1);
6174     // else ???
6175   }
6176   else {
6177     return EXTR_BAD_PATH_SHAPE;
6178   }
6179
6180   return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6181                           theHasRefPoint, theRefPoint, theMakeGroups);
6182 }
6183
6184
6185 //=======================================================================
6186 //function : MakeEdgePathPoints
6187 //purpose  : auxilary for ExtrusionAlongTrack
6188 //=======================================================================
6189 SMESH_MeshEditor::Extrusion_Error
6190 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>&                aPrms,
6191                                      const TopoDS_Edge&                aTrackEdge,
6192                                      bool                              FirstIsStart,
6193                                      list<SMESH_MeshEditor_PathPoint>& LPP)
6194 {
6195   Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6196   aTolVec=1.e-7;
6197   aTolVec2=aTolVec*aTolVec;
6198   double aT1, aT2;
6199   TopoDS_Vertex aV1, aV2;
6200   TopExp::Vertices( aTrackEdge, aV1, aV2 );
6201   aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6202   aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6203   // 2. Collect parameters on the track edge
6204   aPrms.push_front( aT1 );
6205   aPrms.push_back( aT2 );
6206   // sort parameters
6207   aPrms.sort();
6208   if( FirstIsStart ) {
6209     if ( aT1 > aT2 ) {
6210       aPrms.reverse();
6211     }
6212   }
6213   else {
6214     if ( aT2 > aT1 ) {
6215       aPrms.reverse();
6216     }
6217   }
6218   // 3. Path Points
6219   SMESH_MeshEditor_PathPoint aPP;
6220   Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6221   std::list<double>::iterator aItD = aPrms.begin();
6222   for(; aItD != aPrms.end(); ++aItD) {
6223     double aT = *aItD;
6224     gp_Pnt aP3D;
6225     gp_Vec aVec;
6226     aC3D->D1( aT, aP3D, aVec );
6227     aL2 = aVec.SquareMagnitude();
6228     if ( aL2 < aTolVec2 )
6229       return EXTR_CANT_GET_TANGENT;
6230     gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6231     aPP.SetPnt( aP3D );
6232     aPP.SetTangent( aTgt );
6233     aPP.SetParameter( aT );
6234     LPP.push_back(aPP);
6235   }
6236   return EXTR_OK;
6237 }
6238
6239
6240 //=======================================================================
6241 //function : MakeExtrElements
6242 //purpose  : auxilary for ExtrusionAlongTrack
6243 //=======================================================================
6244 SMESH_MeshEditor::Extrusion_Error
6245 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet                  theElemSets[2],
6246                                    list<SMESH_MeshEditor_PathPoint>& fullList,
6247                                    const bool                        theHasAngles,
6248                                    list<double>&                     theAngles,
6249                                    const bool                        theLinearVariation,
6250                                    const bool                        theHasRefPoint,
6251                                    const gp_Pnt&                     theRefPoint,
6252                                    const bool                        theMakeGroups)
6253 {
6254   const int aNbTP = fullList.size();
6255
6256   // Angles
6257   if( theHasAngles && !theAngles.empty() && theLinearVariation )
6258     LinearAngleVariation(aNbTP-1, theAngles);
6259
6260   // fill vector of path points with angles
6261   vector<SMESH_MeshEditor_PathPoint> aPPs;
6262   list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6263   list<double>::iterator                 itAngles = theAngles.begin();
6264   aPPs.push_back( *itPP++ );
6265   for( ; itPP != fullList.end(); itPP++) {
6266     aPPs.push_back( *itPP );
6267     if ( theHasAngles && itAngles != theAngles.end() )
6268       aPPs.back().SetAngle( *itAngles++ );
6269   }
6270
6271   TNodeOfNodeListMap   mapNewNodes;
6272   TElemOfVecOfNnlmiMap mapElemNewNodes;
6273   TTElemOfElemListMap  newElemsMap;
6274   TIDSortedElemSet::iterator itElem;
6275   // source elements for each generated one
6276   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6277
6278   // 3. Center of rotation aV0
6279   gp_Pnt aV0 = theRefPoint;
6280   if ( !theHasRefPoint )
6281   {
6282     gp_XYZ aGC( 0.,0.,0. );
6283     TIDSortedElemSet newNodes;
6284
6285     for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6286     {
6287       TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6288       itElem = theElements.begin();
6289       for ( ; itElem != theElements.end(); itElem++ )
6290       {
6291         const SMDS_MeshElement* elem = *itElem;
6292         SMDS_ElemIteratorPtr     itN = elem->nodesIterator();
6293         while ( itN->more() ) {
6294           const SMDS_MeshElement* node = itN->next();
6295           if ( newNodes.insert( node ).second )
6296             aGC += SMESH_TNodeXYZ( node );
6297         }
6298       }
6299     }
6300     aGC /= newNodes.size();
6301     aV0.SetXYZ( aGC );
6302   } // if (!theHasRefPoint) {
6303
6304   // 4. Processing the elements
6305   SMESHDS_Mesh* aMesh = GetMeshDS();
6306   list<const SMDS_MeshNode*> emptyList;
6307
6308   setElemsFirst( theElemSets );
6309   for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6310   {
6311     TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6312     for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6313     {
6314       const SMDS_MeshElement* elem = *itElem;
6315
6316       vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317       newNodesItVec.reserve( elem->NbNodes() );
6318
6319       // loop on elem nodes
6320       int nodeIndex = -1;
6321       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322       while ( itN->more() )
6323       {
6324         ++nodeIndex;
6325         // check if a node has been already processed
6326         const SMDS_MeshNode* node = cast2Node( itN->next() );
6327         TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6328         list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6329         if ( listNewNodes.empty() )
6330         {
6331           // make new nodes
6332           Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6333           gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6334           gp_Ax1 anAx1, anAxT1T0;
6335           gp_Dir aDT1x, aDT0x, aDT1T0;
6336
6337           aTolAng=1.e-4;
6338
6339           aV0x = aV0;
6340           aPN0 = SMESH_TNodeXYZ( node );
6341
6342           const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6343           aP0x = aPP0.Pnt();
6344           aDT0x= aPP0.Tangent();
6345
6346           for ( int j = 1; j < aNbTP; ++j ) {
6347             const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6348             aP1x     = aPP1.Pnt();
6349             aDT1x    = aPP1.Tangent();
6350             aAngle1x = aPP1.Angle();
6351
6352             gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6353             // Translation
6354             gp_Vec aV01x( aP0x, aP1x );
6355             aTrsf.SetTranslation( aV01x );
6356
6357             // traslated point
6358             aV1x = aV0x.Transformed( aTrsf );
6359             aPN1 = aPN0.Transformed( aTrsf );
6360
6361             // rotation 1 [ T1,T0 ]
6362             aAngleT1T0=-aDT1x.Angle( aDT0x );
6363             if (fabs(aAngleT1T0) > aTolAng)
6364             {
6365               aDT1T0=aDT1x^aDT0x;
6366               anAxT1T0.SetLocation( aV1x );
6367               anAxT1T0.SetDirection( aDT1T0 );
6368               aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6369
6370               aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6371             }
6372
6373             // rotation 2
6374             if ( theHasAngles ) {
6375               anAx1.SetLocation( aV1x );
6376               anAx1.SetDirection( aDT1x );
6377               aTrsfRot.SetRotation( anAx1, aAngle1x );
6378
6379               aPN1 = aPN1.Transformed( aTrsfRot );
6380             }
6381
6382             // make new node
6383             if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6384             {
6385               // create additional node
6386               gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6387               const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6388               myLastCreatedNodes.push_back(newNode);
6389               srcNodes.push_back( node );
6390               listNewNodes.push_back( newNode );
6391             }
6392             const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6393             myLastCreatedNodes.push_back(newNode);
6394             srcNodes.push_back( node );
6395             listNewNodes.push_back( newNode );
6396
6397             aPN0 = aPN1;
6398             aP0x = aP1x;
6399             aV0x = aV1x;
6400             aDT0x = aDT1x;
6401           }
6402         }
6403         else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6404         {
6405           // if current elem is quadratic and current node is not medium
6406           // we have to check - may be it is needed to insert additional nodes
6407           list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6408           if ((int) listNewNodes.size() == aNbTP-1 )
6409           {
6410             vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6411             gp_XYZ P(node->X(), node->Y(), node->Z());
6412             list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6413             int i;
6414             for(i=0; i<aNbTP-1; i++) {
6415               const SMDS_MeshNode* N = *it;
6416               double x = ( N->X() + P.X() )/2.;
6417               double y = ( N->Y() + P.Y() )/2.;
6418               double z = ( N->Z() + P.Z() )/2.;
6419               const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6420               srcNodes.push_back( node );
6421               myLastCreatedNodes.push_back(newN);
6422               aNodes[2*i] = newN;
6423               aNodes[2*i+1] = N;
6424               P = gp_XYZ(N->X(),N->Y(),N->Z());
6425             }
6426             listNewNodes.clear();
6427             for(i=0; i<2*(aNbTP-1); i++) {
6428               listNewNodes.push_back(aNodes[i]);
6429             }
6430           }
6431         }
6432
6433         newNodesItVec.push_back( nIt );
6434       }
6435
6436       // make new elements
6437       sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6438     }
6439   }
6440
6441   makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6442
6443   if ( theMakeGroups )
6444     generateGroups( srcNodes, srcElems, "extruded");
6445
6446   return EXTR_OK;
6447 }
6448
6449
6450 //=======================================================================
6451 //function : LinearAngleVariation
6452 //purpose  : spread values over nbSteps
6453 //=======================================================================
6454
6455 void SMESH_MeshEditor::LinearAngleVariation(const int     nbSteps,
6456                                             list<double>& Angles)
6457 {
6458   int nbAngles = Angles.size();
6459   if( nbSteps > nbAngles && nbAngles > 0 )
6460   {
6461     vector<double> theAngles(nbAngles);
6462     theAngles.assign( Angles.begin(), Angles.end() );
6463
6464     list<double> res;
6465     double rAn2St = double( nbAngles ) / double( nbSteps );
6466     double angPrev = 0, angle;
6467     for ( int iSt = 0; iSt < nbSteps; ++iSt )
6468     {
6469       double angCur = rAn2St * ( iSt+1 );
6470       double angCurFloor  = floor( angCur );
6471       double angPrevFloor = floor( angPrev );
6472       if ( angPrevFloor == angCurFloor )
6473         angle = rAn2St * theAngles[ int( angCurFloor ) ];
6474       else {
6475         int iP = int( angPrevFloor );
6476         double angPrevCeil = ceil(angPrev);
6477         angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6478
6479         int iC = int( angCurFloor );
6480         if ( iC < nbAngles )
6481           angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6482
6483         iP = int( angPrevCeil );
6484         while ( iC-- > iP )
6485           angle += theAngles[ iC ];
6486       }
6487       res.push_back(angle);
6488       angPrev = angCur;
6489     }
6490     Angles.swap( res );
6491   }
6492 }
6493
6494
6495 //================================================================================
6496 /*!
6497  * \brief Move or copy theElements applying theTrsf to their nodes
6498  *  \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6499  *  \param theTrsf - transformation to apply
6500  *  \param theCopy - if true, create translated copies of theElems
6501  *  \param theMakeGroups - if true and theCopy, create translated groups
6502  *  \param theTargetMesh - mesh to copy translated elements into
6503  *  \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6504  */
6505 //================================================================================
6506
6507 SMESH_MeshEditor::PGroupIDs
6508 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6509                              const gp_Trsf&     theTrsf,
6510                              const bool         theCopy,
6511                              const bool         theMakeGroups,
6512                              SMESH_Mesh*        theTargetMesh)
6513 {
6514   ClearLastCreated();
6515   myLastCreatedElems.reserve( theElems.size() );
6516
6517   bool needReverse = false;
6518   string groupPostfix;
6519   switch ( theTrsf.Form() ) {
6520   case gp_PntMirror:
6521     needReverse = true;
6522     groupPostfix = "mirrored";
6523     break;
6524   case gp_Ax1Mirror:
6525     groupPostfix = "mirrored";
6526     break;
6527   case gp_Ax2Mirror:
6528     needReverse = true;
6529     groupPostfix = "mirrored";
6530     break;
6531   case gp_Rotation:
6532     groupPostfix = "rotated";
6533     break;
6534   case gp_Translation:
6535     groupPostfix = "translated";
6536     break;
6537   case gp_Scale:
6538     groupPostfix = "scaled";
6539     break;
6540   case gp_CompoundTrsf: // different scale by axis
6541     groupPostfix = "scaled";
6542     break;
6543   default:
6544     needReverse = false;
6545     groupPostfix = "transformed";
6546   }
6547
6548   SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6549   SMESHDS_Mesh* aMesh    = GetMeshDS();
6550
6551   SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6552   SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6553   SMESH_MeshEditor::ElemFeatures elemType;
6554
6555   // map old node to new one
6556   TNodeNodeMap nodeMap;
6557
6558   // elements sharing moved nodes; those of them which have all
6559   // nodes mirrored but are not in theElems are to be reversed
6560   TIDSortedElemSet inverseElemSet;
6561
6562   // source elements for each generated one
6563   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6564
6565   // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6566   TIDSortedElemSet orphanNode;
6567
6568   if ( theElems.empty() ) // transform the whole mesh
6569   {
6570     // add all elements
6571     SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6572     while ( eIt->more() ) theElems.insert( eIt->next() );
6573     // add orphan nodes
6574     SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6575     while ( nIt->more() )
6576     {
6577       const SMDS_MeshNode* node = nIt->next();
6578       if ( node->NbInverseElements() == 0)
6579         orphanNode.insert( node );
6580     }
6581   }
6582
6583   // loop on elements to transform nodes : first orphan nodes then elems
6584   TIDSortedElemSet::iterator itElem;
6585   TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6586   for (int i=0; i<2; i++)
6587     for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6588     {
6589       const SMDS_MeshElement* elem = *itElem;
6590       if ( !elem )
6591         continue;
6592
6593       // loop on elem nodes
6594       double coord[3];
6595       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6596       while ( itN->more() )
6597       {
6598         const SMDS_MeshNode* node = cast2Node( itN->next() );
6599         // check if a node has been already transformed
6600         pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6601           nodeMap.insert( make_pair ( node, node ));
6602         if ( !n2n_isnew.second )
6603           continue;
6604
6605         node->GetXYZ( coord );
6606         theTrsf.Transforms( coord[0], coord[1], coord[2] );
6607         if ( theTargetMesh ) {
6608           const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6609           n2n_isnew.first->second = newNode;
6610           myLastCreatedNodes.push_back(newNode);
6611           srcNodes.push_back( node );
6612         }
6613         else if ( theCopy ) {
6614           const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6615           n2n_isnew.first->second = newNode;
6616           myLastCreatedNodes.push_back(newNode);
6617           srcNodes.push_back( node );
6618         }
6619         else {
6620           aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6621           // node position on shape becomes invalid
6622           const_cast< SMDS_MeshNode* > ( node )->SetPosition
6623             ( SMDS_SpacePosition::originSpacePosition() );
6624         }
6625
6626         // keep inverse elements
6627         if ( !theCopy && !theTargetMesh && needReverse ) {
6628           SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6629           while ( invElemIt->more() ) {
6630             const SMDS_MeshElement* iel = invElemIt->next();
6631             inverseElemSet.insert( iel );
6632           }
6633         }
6634       }
6635     } // loop on elems in { &orphanNode, &theElems };
6636
6637   // either create new elements or reverse mirrored ones
6638   if ( !theCopy && !needReverse && !theTargetMesh )
6639     return PGroupIDs();
6640
6641   theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6642
6643   // Replicate or reverse elements
6644
6645   std::vector<int> iForw;
6646   vector<const SMDS_MeshNode*> nodes;
6647   for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6648   {
6649     const SMDS_MeshElement* elem = *itElem;
6650     if ( !elem ) continue;
6651
6652     SMDSAbs_GeometryType geomType = elem->GetGeomType();
6653     size_t               nbNodes  = elem->NbNodes();
6654     if ( geomType == SMDSGeom_NONE ) continue; // node
6655
6656     nodes.resize( nbNodes );
6657
6658     if ( geomType == SMDSGeom_POLYHEDRA )  // ------------------ polyhedral volume
6659     {
6660       const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6661       if (!aPolyedre)
6662         continue;
6663       nodes.clear();
6664       bool allTransformed = true;
6665       int nbFaces = aPolyedre->NbFaces();
6666       for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6667       {
6668         int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6669         for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6670         {
6671           const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6672           TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673           if ( nodeMapIt == nodeMap.end() )
6674             allTransformed = false; // not all nodes transformed
6675           else
6676             nodes.push_back((*nodeMapIt).second);
6677         }
6678         if ( needReverse && allTransformed )
6679           std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6680       }
6681       if ( !allTransformed )
6682         continue; // not all nodes transformed
6683     }
6684     else // ----------------------- the rest element types
6685     {
6686       while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6687       const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6688       const vector<int>&    i = needReverse ? iRev : iForw;
6689
6690       // find transformed nodes
6691       size_t iNode = 0;
6692       SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6693       while ( itN->more() ) {
6694         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6695         TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6696         if ( nodeMapIt == nodeMap.end() )
6697           break; // not all nodes transformed
6698         nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6699       }
6700       if ( iNode != nbNodes )
6701         continue; // not all nodes transformed
6702     }
6703
6704     if ( editor ) {
6705       // copy in this or a new mesh
6706       if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6707         srcElems.push_back( elem );
6708     }
6709     else {
6710       // reverse element as it was reversed by transformation
6711       if ( nbNodes > 2 )
6712         aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6713     }
6714
6715   } // loop on elements
6716
6717   if ( editor && editor != this )
6718     myLastCreatedElems.swap( editor->myLastCreatedElems );
6719
6720   PGroupIDs newGroupIDs;
6721
6722   if ( ( theMakeGroups && theCopy ) ||
6723        ( theMakeGroups && theTargetMesh ) )
6724     newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6725
6726   return newGroupIDs;
6727 }
6728
6729 //================================================================================
6730 /*!
6731  * \brief Make an offset mesh from a source 2D mesh
6732  *  \param [in] theElements - source faces
6733  *  \param [in] theValue - offset value
6734  *  \param [out] theTgtMesh - a mesh to add offset elements to
6735  *  \param [in] theMakeGroups - to generate groups
6736  *  \return PGroupIDs - IDs of created groups
6737  */
6738 //================================================================================
6739
6740 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6741                                                       const double       theValue,
6742                                                       SMESH_Mesh*        theTgtMesh,
6743                                                       const bool         theMakeGroups,
6744                                                       const bool         theFixSelfIntersection)
6745 {
6746   SMESHDS_Mesh*    meshDS = GetMeshDS();
6747   SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6748   SMESH_MeshEditor tgtEditor( theTgtMesh );
6749
6750   SMDS_ElemIteratorPtr eIt;
6751   if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6752   else                       eIt = SMESHUtils::elemSetIterator( theElements );
6753
6754   SMESH_MeshAlgos::TEPairVec new2OldFaces;
6755   SMESH_MeshAlgos::TNPairVec new2OldNodes;
6756   std::unique_ptr< SMDS_Mesh > offsetMesh
6757     ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6758                                    theFixSelfIntersection,
6759                                    new2OldFaces, new2OldNodes ));
6760
6761   offsetMesh->Modified();
6762   offsetMesh->CompactMesh(); // make IDs start from 1
6763
6764   // source elements for each generated one
6765   SMESH_SequenceOfElemPtr srcElems, srcNodes;
6766   srcElems.reserve( new2OldFaces.size() );
6767   srcNodes.reserve( new2OldNodes.size() );
6768
6769   ClearLastCreated();
6770   myLastCreatedElems.reserve( new2OldFaces.size() );
6771   myLastCreatedNodes.reserve( new2OldNodes.size() );
6772
6773   // copy offsetMesh to theTgtMesh
6774
6775   int idShift = meshDS->MaxNodeID();
6776   for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6777     if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6778     {
6779       if ( n->NbInverseElements() > 0 )
6780       {
6781         const SMDS_MeshNode* n2 =
6782           tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6783         myLastCreatedNodes.push_back( n2 );
6784         srcNodes.push_back( new2OldNodes[ i ].second );
6785       }
6786     }
6787
6788   ElemFeatures elemType;
6789   for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6790     if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6791     {
6792       elemType.Init( f );
6793       elemType.myNodes.clear();
6794       for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6795       {
6796         const SMDS_MeshNode* n2 = nIt->next();
6797         elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6798       }
6799       tgtEditor.AddElement( elemType.myNodes, elemType );
6800       srcElems.push_back( new2OldFaces[ i ].second );
6801     }
6802
6803   myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6804
6805   PGroupIDs newGroupIDs;
6806   if ( theMakeGroups )
6807     newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6808
6809   return newGroupIDs;
6810 }
6811
6812 //=======================================================================
6813 /*!
6814  * \brief Create groups of elements made during transformation
6815  *  \param nodeGens - nodes making corresponding myLastCreatedNodes
6816  *  \param elemGens - elements making corresponding myLastCreatedElems
6817  *  \param postfix - to push_back to names of new groups
6818  *  \param targetMesh - mesh to create groups in
6819  *  \param topPresent - is there are "top" elements that are created by sweeping
6820  */
6821 //=======================================================================
6822
6823 SMESH_MeshEditor::PGroupIDs
6824 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6825                                  const SMESH_SequenceOfElemPtr& elemGens,
6826                                  const std::string&             postfix,
6827                                  SMESH_Mesh*                    targetMesh,
6828                                  const bool                     topPresent)
6829 {
6830   PGroupIDs newGroupIDs( new list<int> );
6831   SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6832
6833   // Sort existing groups by types and collect their names
6834
6835   // containers to store an old group and generated new ones;
6836   // 1st new group is for result elems of different type than a source one;
6837   // 2nd new group is for same type result elems ("top" group at extrusion)
6838   using boost::tuple;
6839   using boost::make_tuple;
6840   typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6841   vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6842   vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6843   // group names
6844   set< string > groupNames;
6845
6846   SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6847   if ( !groupIt->more() ) return newGroupIDs;
6848
6849   int newGroupID = mesh->GetGroupIds().back()+1;
6850   while ( groupIt->more() )
6851   {
6852     SMESH_Group * group = groupIt->next();
6853     if ( !group ) continue;
6854     SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6855     if ( !groupDS || groupDS->IsEmpty() ) continue;
6856     groupNames.insert    ( group->GetName() );
6857     groupDS->SetStoreName( group->GetName() );
6858     const SMDSAbs_ElementType type = groupDS->GetType();
6859     SMESHDS_Group* newGroup    = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6860     SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6861     groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6862     orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6863   }
6864
6865   // Loop on nodes and elements to add them in new groups
6866
6867   vector< const SMDS_MeshElement* > resultElems;
6868   for ( int isNodes = 0; isNodes < 2; ++isNodes )
6869   {
6870     const SMESH_SequenceOfElemPtr& gens  = isNodes ? nodeGens : elemGens;
6871     const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6872     if ( gens.size() != elems.size() )
6873       throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6874
6875     // loop on created elements
6876     for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6877     {
6878       const SMDS_MeshElement* sourceElem = gens[ iElem ];
6879       if ( !sourceElem ) {
6880         MESSAGE("generateGroups(): NULL source element");
6881         continue;
6882       }
6883       list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6884       if ( groupsOldNew.empty() ) { // no groups of this type at all
6885         while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6886           ++iElem; // skip all elements made by sourceElem
6887         continue;
6888       }
6889       // collect all elements made by the iElem-th sourceElem
6890       resultElems.clear();
6891       if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6892         if ( resElem != sourceElem )
6893           resultElems.push_back( resElem );
6894       while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6895         if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6896           if ( resElem != sourceElem )
6897             resultElems.push_back( resElem );
6898
6899       const SMDS_MeshElement* topElem = 0;
6900       if ( isNodes ) // there must be a top element
6901       {
6902         topElem = resultElems.back();
6903         resultElems.pop_back();
6904       }
6905       else
6906       {
6907         vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6908         for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6909           if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6910           {
6911             topElem = *resElemIt;
6912             *resElemIt = 0; // erase *resElemIt
6913             break;
6914           }
6915       }
6916       // add resultElems to groups originted from ones the sourceElem belongs to
6917       list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6918       for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6919       {
6920         SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6921         if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6922         {
6923           // fill in a new group
6924           SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6925           vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6926           for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6927             if ( *resElemIt )
6928               newGroup.Add( *resElemIt );
6929
6930           // fill a "top" group
6931           if ( topElem )
6932           {
6933             SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6934             newTopGroup.Add( topElem );
6935           }
6936         }
6937       }
6938     } // loop on created elements
6939   }// loop on nodes and elements
6940
6941   // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6942
6943   list<int> topGrouIds;
6944   for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6945   {
6946     SMESHDS_GroupBase* oldGroupDS =   orderedOldNewGroups[i]->get<0>();
6947     SMESHDS_Group*   newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6948                                       orderedOldNewGroups[i]->get<2>() };
6949     for ( int is2nd = 0; is2nd < 2; ++is2nd )
6950     {
6951       SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6952       if ( newGroupDS->IsEmpty() )
6953       {
6954         mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6955       }
6956       else
6957       {
6958         // set group type
6959         newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6960
6961         // make a name
6962         const bool isTop = ( topPresent &&
6963                              newGroupDS->GetType() == oldGroupDS->GetType() &&
6964                              is2nd );
6965
6966         string name = oldGroupDS->GetStoreName();
6967         { // remove trailing whitespaces (issue 22599)
6968           size_t size = name.size();
6969           while ( size > 1 && isspace( name[ size-1 ]))
6970             --size;
6971           if ( size != name.size() )
6972           {
6973             name.resize( size );
6974             oldGroupDS->SetStoreName( name.c_str() );
6975           }
6976         }
6977         if ( !targetMesh ) {
6978           string suffix = ( isTop ? "top": postfix.c_str() );
6979           name += "_";
6980           name += suffix;
6981           int nb = 1;
6982           while ( !groupNames.insert( name ).second ) // name exists
6983             name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6984         }
6985         else if ( isTop ) {
6986           name += "_top";
6987         }
6988         newGroupDS->SetStoreName( name.c_str() );
6989
6990         // make a SMESH_Groups
6991         mesh->AddGroup( newGroupDS );
6992         if ( isTop )
6993           topGrouIds.push_back( newGroupDS->GetID() );
6994         else
6995           newGroupIDs->push_back( newGroupDS->GetID() );
6996       }
6997     }
6998   }
6999   newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7000
7001   return newGroupIDs;
7002 }
7003
7004 //================================================================================
7005 /*!
7006  *  * \brief Return list of group of nodes close to each other within theTolerance
7007  *  *        Search among theNodes or in the whole mesh if theNodes is empty using
7008  *  *        an Octree algorithm
7009  *  \param [in,out] theNodes - the nodes to treat
7010  *  \param [in]     theTolerance - the tolerance
7011  *  \param [out]    theGroupsOfNodes - the result groups of coincident nodes
7012  *  \param [in]     theSeparateCornersAndMedium - if \c true, in quadratic mesh puts 
7013  *         corner and medium nodes in separate groups
7014  */
7015 //================================================================================
7016
7017 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet &   theNodes,
7018                                             const double         theTolerance,
7019                                             TListOfListOfNodes & theGroupsOfNodes,
7020                                             bool                 theSeparateCornersAndMedium)
7021 {
7022   ClearLastCreated();
7023
7024   if ( myMesh->NbEdges  ( ORDER_QUADRATIC ) +
7025        myMesh->NbFaces  ( ORDER_QUADRATIC ) +
7026        myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7027     theSeparateCornersAndMedium = false;
7028
7029   TIDSortedNodeSet& corners = theNodes;
7030   TIDSortedNodeSet  medium;
7031
7032   if ( theNodes.empty() ) // get all nodes in the mesh
7033   {
7034     TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7035     SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7036     if ( theSeparateCornersAndMedium )
7037       while ( nIt->more() )
7038       {
7039         const SMDS_MeshNode* n = nIt->next();
7040         TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7041         nodeSet->insert( nodeSet->end(), n );
7042       }
7043     else
7044       while ( nIt->more() )
7045         theNodes.insert( theNodes.end(),nIt->next() );
7046   }
7047   else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7048   {
7049     TIDSortedNodeSet::iterator nIt = corners.begin();
7050     while ( nIt != corners.end() )
7051       if ( SMESH_MesherHelper::IsMedium( *nIt ))
7052       {
7053         medium.insert( medium.end(), *nIt );
7054         corners.erase( nIt++ );
7055       }
7056       else
7057       {
7058         ++nIt;
7059       }
7060   }
7061
7062   if ( !corners.empty() )
7063     SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7064   if ( !medium.empty() )
7065     SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7066 }
7067
7068 //=======================================================================
7069 //function : SimplifyFace
7070 //purpose  : split a chain of nodes into several closed chains
7071 //=======================================================================
7072
7073 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7074                                     vector<const SMDS_MeshNode *>&       poly_nodes,
7075                                     vector<int>&                         quantities) const
7076 {
7077   int nbNodes = faceNodes.size();
7078   while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7079     --nbNodes;
7080   if ( nbNodes < 3 )
7081     return 0;
7082   size_t prevNbQuant = quantities.size();
7083
7084   vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7085   map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7086   map< const SMDS_MeshNode*, int >::iterator nInd;
7087
7088   nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7089   simpleNodes.push_back( faceNodes[0] );
7090   for ( int iCur = 1; iCur < nbNodes; iCur++ )
7091   {
7092     if ( faceNodes[ iCur ] != simpleNodes.back() )
7093     {
7094       int index = simpleNodes.size();
7095       nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7096       int prevIndex = nInd->second;
7097       if ( prevIndex < index )
7098       {
7099         // a sub-loop found
7100         int loopLen = index - prevIndex;
7101         if ( loopLen > 2 )
7102         {
7103           // store the sub-loop
7104           quantities.push_back( loopLen );
7105           for ( int i = prevIndex; i < index; i++ )
7106             poly_nodes.push_back( simpleNodes[ i ]);
7107         }
7108         simpleNodes.resize( prevIndex+1 );
7109       }
7110       else
7111       {
7112         simpleNodes.push_back( faceNodes[ iCur ]);
7113       }
7114     }
7115   }
7116
7117   if ( simpleNodes.size() > 2 )
7118   {
7119     quantities.push_back( simpleNodes.size() );
7120     poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7121   }
7122
7123   return quantities.size() - prevNbQuant;
7124 }
7125
7126 //=======================================================================
7127 //function : MergeNodes
7128 //purpose  : In each group, the cdr of nodes are substituted by the first one
7129 //           in all elements.
7130 //=======================================================================
7131
7132 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7133 {
7134   ClearLastCreated();
7135
7136   SMESHDS_Mesh* aMesh = GetMeshDS();
7137
7138   TNodeNodeMap nodeNodeMap; // node to replace - new node
7139   set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7140   list< int > rmElemIds, rmNodeIds;
7141
7142   // Fill nodeNodeMap and elems
7143
7144   TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7145   for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7146   {
7147     list<const SMDS_MeshNode*>& nodes = *grIt;
7148     list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7149     const SMDS_MeshNode* nToKeep = *nIt;
7150     for ( ++nIt; nIt != nodes.end(); nIt++ )
7151     {
7152       const SMDS_MeshNode* nToRemove = *nIt;
7153       nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7154       if ( nToRemove != nToKeep )
7155       {
7156         rmNodeIds.push_back( nToRemove->GetID() );
7157         AddToSameGroups( nToKeep, nToRemove, aMesh );
7158         // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7159         // after MergeNodes() w/o creating node in place of merged ones.
7160         const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7161         if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7162           if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7163             sm->SetIsAlwaysComputed( true );
7164       }
7165       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7166       while ( invElemIt->more() ) {
7167         const SMDS_MeshElement* elem = invElemIt->next();
7168         elems.insert(elem);
7169       }
7170     }
7171   }
7172   // Change element nodes or remove an element
7173
7174   set<const SMDS_MeshNode*> nodeSet;
7175   vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7176   vector<int> iRepl;
7177   ElemFeatures elemType;
7178
7179   set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7180   for ( ; eIt != elems.end(); eIt++ )
7181   {
7182     const SMDS_MeshElement* elem = *eIt;
7183     const           int  nbNodes = elem->NbNodes();
7184     const           int aShapeId = FindShape( elem );
7185     SMDSAbs_EntityType    entity = elem->GetEntityType();
7186
7187     nodeSet.clear();
7188     curNodes.resize( nbNodes );
7189     uniqueNodes.resize( nbNodes );
7190     iRepl.resize( nbNodes );
7191     int iUnique = 0, iCur = 0, nbRepl = 0;
7192
7193     // get new seq of nodes
7194     SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7195     while ( itN->more() )
7196     {
7197       const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7198
7199       TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7200       if ( nnIt != nodeNodeMap.end() ) { // n sticks
7201         n = (*nnIt).second;
7202         { ////////// BUG 0020185: begin
7203           bool stopRecur = false;
7204           set<const SMDS_MeshNode*> nodesRecur;
7205           nodesRecur.insert(n);
7206           while (!stopRecur) {
7207             TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7208             if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7209               n = (*nnIt_i).second;
7210               if (!nodesRecur.insert(n).second) {
7211                 // error: recursive dependency
7212                 stopRecur = true;
7213               }
7214             }
7215             else
7216               stopRecur = true;
7217           }
7218         } ////////// BUG 0020185: end
7219       }
7220       curNodes[ iCur ] = n;
7221       bool isUnique = nodeSet.insert( n ).second;
7222       if ( isUnique )
7223         uniqueNodes[ iUnique++ ] = n;
7224       else
7225         iRepl[ nbRepl++ ] = iCur;
7226       iCur++;
7227     }
7228
7229     // Analyse element topology after replacement
7230
7231     bool isOk = true;
7232     int nbUniqueNodes = nodeSet.size();
7233     if ( nbNodes != nbUniqueNodes ) // some nodes stick
7234     {
7235       if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7236       {
7237         if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7238         {
7239           elemType.Init( elem );
7240           const bool isQuad = elemType.myIsQuad;
7241           if ( isQuad )
7242             SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7243               ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7244
7245           // a polygon can divide into several elements
7246           vector<const SMDS_MeshNode *> polygons_nodes;
7247           vector<int> quantities;
7248           int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7249           if (nbNew > 0)
7250           {
7251             vector<const SMDS_MeshNode *> face_nodes;
7252             int inode = 0;
7253             for (int iface = 0; iface < nbNew; iface++)
7254             {
7255               int nbNewNodes = quantities[iface];
7256               face_nodes.assign( polygons_nodes.begin() + inode,
7257                                  polygons_nodes.begin() + inode + nbNewNodes );
7258               inode += nbNewNodes;
7259               if ( isQuad ) // check if a result elem is a valid quadratic polygon
7260               {
7261                 bool isValid = ( nbNewNodes % 2 == 0 );
7262                 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7263                   isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7264                 elemType.SetQuad( isValid );
7265                 if ( isValid ) // put medium nodes after corners
7266                   SMDS_MeshCell::applyInterlaceRev
7267                     ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7268                                                           nbNewNodes ), face_nodes );
7269               }
7270               elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7271
7272               SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7273               if ( aShapeId )
7274                 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7275             }
7276           }
7277           rmElemIds.push_back(elem->GetID());
7278
7279         } // Polygon
7280
7281         else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7282         {
7283           if ( nbUniqueNodes < 4 ) {
7284             rmElemIds.push_back(elem->GetID());
7285           }
7286           else {
7287             // each face has to be analyzed in order to check volume validity
7288             const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7289             if ( aPolyedre )
7290             {
7291               int nbFaces = aPolyedre->NbFaces();
7292
7293               vector<const SMDS_MeshNode *> poly_nodes;
7294               vector<int>                   quantities;
7295               vector<const SMDS_MeshNode *> faceNodes;
7296
7297               for (int iface = 1; iface <= nbFaces; iface++)
7298               {
7299                 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7300                 faceNodes.resize( nbFaceNodes );
7301                 for (int inode = 1; inode <= nbFaceNodes; inode++)
7302                 {
7303                   const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7304                   TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7305                   if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7306                     faceNode = (*nnIt).second;
7307                   faceNodes[inode - 1] = faceNode;
7308                 }
7309                 SimplifyFace(faceNodes, poly_nodes, quantities);
7310               }
7311
7312               if ( quantities.size() > 3 ) {
7313                 // TODO: remove coincident faces
7314               }
7315
7316               if ( quantities.size() > 3 )
7317               {
7318                 const SMDS_MeshElement* newElem =
7319                   aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7320                 myLastCreatedElems.push_back( newElem );
7321                 if ( aShapeId && newElem )
7322                   aMesh->SetMeshElementOnShape( newElem, aShapeId );
7323                 rmElemIds.push_back( elem->GetID() );
7324               }
7325             }
7326             else {
7327               rmElemIds.push_back( elem->GetID() );
7328             }
7329           }
7330         }
7331         else {
7332         }
7333
7334         continue;
7335       } // poly element
7336
7337       // Regular elements
7338       // TODO not all the possible cases are solved. Find something more generic?
7339       switch ( entity ) {
7340       case SMDSEntity_Edge: //////// EDGE
7341       case SMDSEntity_Triangle: //// TRIANGLE
7342       case SMDSEntity_Quad_Triangle:
7343       case SMDSEntity_Tetra:
7344       case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7345       {
7346         isOk = false;
7347         break;
7348       }
7349       case SMDSEntity_Quad_Edge:
7350       {
7351         isOk = false; // to linear EDGE ???????
7352         break;
7353       }
7354       case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7355       {
7356         if ( nbUniqueNodes < 3 )
7357           isOk = false;
7358         else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7359           isOk = false; // opposite nodes stick
7360         break;
7361       }
7362       case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7363       {
7364         //   1    5    2
7365         //    +---+---+
7366         //    |       |
7367         //   4+       +6
7368         //    |       |
7369         //    +---+---+
7370         //   0    7    3
7371         if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7372             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7373              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7374              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7375              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7376         {
7377           isOk = true;
7378         }
7379         break;
7380       }
7381       case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7382       {
7383         //   1    5    2
7384         //    +---+---+
7385         //    |       |
7386         //   4+  8+   +6
7387         //    |       |
7388         //    +---+---+
7389         //   0    7    3
7390         if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7391             (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7392              ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7393              ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7394              ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7395         {
7396           isOk = true;
7397         }
7398         break;
7399       }
7400       case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7401       {
7402         isOk = false;
7403         if ( nbUniqueNodes == 4 ) {
7404           // ---------------------------------> tetrahedron
7405           if ( curNodes[3] == curNodes[4] &&
7406                curNodes[3] == curNodes[5] ) {
7407             // top nodes stick
7408             isOk = true;
7409           }
7410           else if ( curNodes[0] == curNodes[1] &&
7411                     curNodes[0] == curNodes[2] ) {
7412             // bottom nodes stick: set a top before
7413             uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7414             uniqueNodes[ 0 ] = curNodes [ 5 ];
7415             uniqueNodes[ 1 ] = curNodes [ 4 ];
7416             uniqueNodes[ 2 ] = curNodes [ 3 ];
7417             isOk = true;
7418           }
7419           else if (( curNodes[0] == curNodes[3] ) +
7420                    ( curNodes[1] == curNodes[4] ) +
7421                    ( curNodes[2] == curNodes[5] ) == 2 ) {
7422             // a lateral face turns into a line
7423             isOk = true;
7424           }
7425         }
7426         else if ( nbUniqueNodes == 5 ) {
7427           // PENTAHEDRON --------------------> pyramid
7428           if ( curNodes[0] == curNodes[3] )
7429           {
7430             uniqueNodes[ 0 ] = curNodes[ 1 ];
7431             uniqueNodes[ 1 ] = curNodes[ 4 ];
7432             uniqueNodes[ 2 ] = curNodes[ 5 ];
7433             uniqueNodes[ 3 ] = curNodes[ 2 ];
7434             uniqueNodes[ 4 ] = curNodes[ 0 ];
7435             isOk = true;
7436           }
7437           if ( curNodes[1] == curNodes[4] )
7438           {
7439             uniqueNodes[ 0 ] = curNodes[ 0 ];
7440             uniqueNodes[ 1 ] = curNodes[ 2 ];
7441             uniqueNodes[ 2 ] = curNodes[ 5 ];
7442             uniqueNodes[ 3 ] = curNodes[ 3 ];
7443             uniqueNodes[ 4 ] = curNodes[ 1 ];
7444             isOk = true;
7445           }
7446           if ( curNodes[2] == curNodes[5] )
7447           {
7448             uniqueNodes[ 0 ] = curNodes[ 0 ];
7449             uniqueNodes[ 1 ] = curNodes[ 3 ];
7450             uniqueNodes[ 2 ] = curNodes[ 4 ];
7451             uniqueNodes[ 3 ] = curNodes[ 1 ];
7452             uniqueNodes[ 4 ] = curNodes[ 2 ];
7453             isOk = true;
7454           }
7455         }
7456         break;
7457       }
7458       case SMDSEntity_Hexa:
7459       {
7460         //////////////////////////////////// HEXAHEDRON
7461         isOk = false;
7462         SMDS_VolumeTool hexa (elem);
7463         hexa.SetExternalNormal();
7464         if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7465           //////////////////////// HEX ---> tetrahedron
7466           for ( int iFace = 0; iFace < 6; iFace++ ) {
7467             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7468             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7469                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7470                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7471               // one face turns into a point ...
7472               int  pickInd = ind[ 0 ];
7473               int iOppFace = hexa.GetOppFaceIndex( iFace );
7474               ind = hexa.GetFaceNodesIndices( iOppFace );
7475               int nbStick = 0;
7476               uniqueNodes.clear();
7477               for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7478                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7479                   nbStick++;
7480                 else
7481                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7482               }
7483               if ( nbStick == 1 ) {
7484                 // ... and the opposite one - into a triangle.
7485                 // set a top node
7486                 uniqueNodes.push_back( curNodes[ pickInd ]);
7487                 isOk = true;
7488               }
7489               break;
7490             }
7491           }
7492         }
7493         else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7494           //////////////////////// HEX ---> prism
7495           int nbTria = 0, iTria[3];
7496           const int *ind; // indices of face nodes
7497           // look for triangular faces
7498           for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7499             ind = hexa.GetFaceNodesIndices( iFace );
7500             TIDSortedNodeSet faceNodes;
7501             for ( iCur = 0; iCur < 4; iCur++ )
7502               faceNodes.insert( curNodes[ind[iCur]] );
7503             if ( faceNodes.size() == 3 )
7504               iTria[ nbTria++ ] = iFace;
7505           }
7506           // check if triangles are opposite
7507           if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7508           {
7509             // set nodes of the bottom triangle
7510             ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7511             vector<int> indB;
7512             for ( iCur = 0; iCur < 4; iCur++ )
7513               if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7514                 indB.push_back( ind[iCur] );
7515             if ( !hexa.IsForward() )
7516               std::swap( indB[0], indB[2] );
7517             for ( iCur = 0; iCur < 3; iCur++ )
7518               uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7519             // set nodes of the top triangle
7520             const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7521             for ( iCur = 0; iCur < 3; ++iCur )
7522               for ( int j = 0; j < 4; ++j )
7523                 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7524                 {
7525                   uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7526                   break;
7527                 }
7528             isOk = true;
7529             break;
7530           }
7531         }
7532         else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7533           //////////////////// HEXAHEDRON ---> pyramid
7534           for ( int iFace = 0; iFace < 6; iFace++ ) {
7535             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7536             if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7537                 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7538                 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7539               // one face turns into a point ...
7540               int iOppFace = hexa.GetOppFaceIndex( iFace );
7541               ind = hexa.GetFaceNodesIndices( iOppFace );
7542               uniqueNodes.clear();
7543               for ( iCur = 0; iCur < 4; iCur++ ) {
7544                 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7545                   break;
7546                 else
7547                   uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7548               }
7549               if ( uniqueNodes.size() == 4 ) {
7550                 // ... and the opposite one is a quadrangle
7551                 // set a top node
7552                 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7553                 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7554                 isOk = true;
7555               }
7556               break;
7557             }
7558           }
7559         }
7560
7561         if ( !isOk && nbUniqueNodes > 4 ) {
7562           ////////////////// HEXAHEDRON ---> polyhedron
7563           hexa.SetExternalNormal();
7564           vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7565           vector<int>                   quantities; quantities.reserve( 6 );
7566           for ( int iFace = 0; iFace < 6; iFace++ )
7567           {
7568             const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7569             if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7570                  curNodes[ind[1]] == curNodes[ind[3]] )
7571             {
7572               quantities.clear();
7573               break; // opposite nodes stick
7574             }
7575             nodeSet.clear();
7576             for ( iCur = 0; iCur < 4; iCur++ )
7577             {
7578               if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7579                 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7580             }
7581             if ( nodeSet.size() < 3 )
7582               poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7583             else
7584               quantities.push_back( nodeSet.size() );
7585           }
7586           if ( quantities.size() >= 4 )
7587           {
7588             const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7589             myLastCreatedElems.push_back( newElem );
7590             if ( aShapeId && newElem )
7591               aMesh->SetMeshElementOnShape( newElem, aShapeId );
7592             rmElemIds.push_back( elem->GetID() );
7593           }
7594         }
7595         break;
7596       } // case HEXAHEDRON
7597
7598       default:
7599         isOk = false;
7600       } // switch ( nbNodes )
7601
7602     } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7603
7604     if ( isOk ) // a non-poly elem remains valid after sticking nodes
7605     {
7606       if ( nbNodes != nbUniqueNodes ||
7607            !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7608       {
7609         elemType.Init( elem ).SetID( elem->GetID() );
7610
7611         SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7612         aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7613
7614         uniqueNodes.resize(nbUniqueNodes);
7615         SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7616         if ( sm && newElem )
7617           sm->AddElement( newElem );
7618         if ( elem != newElem )
7619           ReplaceElemInGroups( elem, newElem, aMesh );
7620       }
7621     }
7622     else {
7623       // Remove invalid regular element or invalid polygon
7624       rmElemIds.push_back( elem->GetID() );
7625     }
7626
7627   } // loop on elements
7628
7629   // Remove bad elements, then equal nodes (order important)
7630
7631   Remove( rmElemIds, false );
7632   Remove( rmNodeIds, true );
7633
7634   return;
7635 }
7636
7637
7638 // ========================================================
7639 // class   : SortableElement
7640 // purpose : allow sorting elements basing on their nodes
7641 // ========================================================
7642 class SortableElement : public set <const SMDS_MeshElement*>
7643 {
7644 public:
7645
7646   SortableElement( const SMDS_MeshElement* theElem )
7647   {
7648     myElem = theElem;
7649     SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7650     while ( nodeIt->more() )
7651       this->insert( nodeIt->next() );
7652   }
7653
7654   const SMDS_MeshElement* Get() const
7655   { return myElem; }
7656
7657 private:
7658   mutable const SMDS_MeshElement* myElem;
7659 };
7660
7661 //=======================================================================
7662 //function : FindEqualElements
7663 //purpose  : Return list of group of elements built on the same nodes.
7664 //           Search among theElements or in the whole mesh if theElements is empty
7665 //=======================================================================
7666
7667 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet &        theElements,
7668                                          TListOfListOfElementsID & theGroupsOfElementsID)
7669 {
7670   ClearLastCreated();
7671
7672   typedef map< SortableElement, int > TMapOfNodeSet;
7673   typedef list<int> TGroupOfElems;
7674
7675   SMDS_ElemIteratorPtr elemIt;
7676   if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7677   else                       elemIt = SMESHUtils::elemSetIterator( theElements );
7678
7679   vector< TGroupOfElems > arrayOfGroups;
7680   TGroupOfElems groupOfElems;
7681   TMapOfNodeSet mapOfNodeSet;
7682
7683   for ( int i = 0; elemIt->more(); )
7684   {
7685     const SMDS_MeshElement* curElem = elemIt->next();
7686     SortableElement SE(curElem);
7687     // check uniqueness
7688     pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7689     if ( !pp.second ) { // one more coincident elem
7690       TMapOfNodeSet::iterator& itSE = pp.first;
7691       int ind = (*itSE).second;
7692       arrayOfGroups[ind].push_back( curElem->GetID() );
7693     }
7694     else {
7695       arrayOfGroups.push_back( groupOfElems );
7696       arrayOfGroups.back().push_back( curElem->GetID() );
7697       i++;
7698     }
7699   }
7700
7701   groupOfElems.clear();
7702   vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7703   for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7704   {
7705     if ( groupIt->size() > 1 ) {
7706       //groupOfElems.sort(); -- theElements is sorted already
7707       theGroupsOfElementsID.push_back( groupOfElems );
7708       theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7709     }
7710   }
7711 }
7712
7713 //=======================================================================
7714 //function : MergeElements
7715 //purpose  : In each given group, substitute all elements by the first one.
7716 //=======================================================================
7717
7718 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7719 {
7720   ClearLastCreated();
7721
7722   typedef list<int> TListOfIDs;
7723   TListOfIDs rmElemIds; // IDs of elems to remove
7724
7725   SMESHDS_Mesh* aMesh = GetMeshDS();
7726
7727   TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7728   while ( groupsIt != theGroupsOfElementsID.end() ) {
7729     TListOfIDs& aGroupOfElemID = *groupsIt;
7730     aGroupOfElemID.sort();
7731     int elemIDToKeep = aGroupOfElemID.front();
7732     const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7733     aGroupOfElemID.pop_front();
7734     TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7735     while ( idIt != aGroupOfElemID.end() ) {
7736       int elemIDToRemove = *idIt;
7737       const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7738       // add the kept element in groups of removed one (PAL15188)
7739       AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7740       rmElemIds.push_back( elemIDToRemove );
7741       ++idIt;
7742     }
7743     ++groupsIt;
7744   }
7745
7746   Remove( rmElemIds, false );
7747 }
7748
7749 //=======================================================================
7750 //function : MergeEqualElements
7751 //purpose  : Remove all but one of elements built on the same nodes.
7752 //=======================================================================
7753
7754 void SMESH_MeshEditor::MergeEqualElements()
7755 {
7756   TIDSortedElemSet aMeshElements; /* empty input ==
7757                                      to merge equal elements in the whole mesh */
7758   TListOfListOfElementsID aGroupsOfElementsID;
7759   FindEqualElements(aMeshElements, aGroupsOfElementsID);
7760   MergeElements(aGroupsOfElementsID);
7761 }
7762
7763 //=======================================================================
7764 //function : findAdjacentFace
7765 //purpose  :
7766 //=======================================================================
7767
7768 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7769                                                 const SMDS_MeshNode* n2,
7770                                                 const SMDS_MeshElement* elem)
7771 {
7772   TIDSortedElemSet elemSet, avoidSet;
7773   if ( elem )
7774     avoidSet.insert ( elem );
7775   return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7776 }
7777
7778 //=======================================================================
7779 //function : findSegment
7780 //purpose  : Return a mesh segment by two nodes one of which can be medium
7781 //=======================================================================
7782
7783 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7784                                            const SMDS_MeshNode* n2)
7785 {
7786   SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7787   while ( it->more() )
7788   {
7789     const SMDS_MeshElement* seg = it->next();
7790     if ( seg->GetNodeIndex( n2 ) >= 0 )
7791       return seg;
7792   }
7793   return 0;
7794 }
7795
7796 //=======================================================================
7797 //function : FindFreeBorder
7798 //purpose  :
7799 //=======================================================================
7800
7801 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7802
7803 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode*             theFirstNode,
7804                                        const SMDS_MeshNode*             theSecondNode,
7805                                        const SMDS_MeshNode*             theLastNode,
7806                                        list< const SMDS_MeshNode* > &   theNodes,
7807                                        list< const SMDS_MeshElement* >& theFaces)
7808 {
7809   if ( !theFirstNode || !theSecondNode )
7810     return false;
7811   // find border face between theFirstNode and theSecondNode
7812   const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7813   if ( !curElem )
7814     return false;
7815
7816   theFaces.push_back( curElem );
7817   theNodes.push_back( theFirstNode );
7818   theNodes.push_back( theSecondNode );
7819
7820   const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7821   TIDSortedElemSet foundElems;
7822   bool needTheLast = ( theLastNode != 0 );
7823
7824   while ( nStart != theLastNode ) {
7825     if ( nStart == theFirstNode )
7826       return !needTheLast;
7827
7828     // find all free border faces sharing form nStart
7829
7830     list< const SMDS_MeshElement* > curElemList;
7831     list< const SMDS_MeshNode* >    nStartList;
7832     SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7833     while ( invElemIt->more() ) {
7834       const SMDS_MeshElement* e = invElemIt->next();
7835       if ( e == curElem || foundElems.insert( e ).second ) {
7836         // get nodes
7837         int iNode = 0, nbNodes = e->NbNodes();
7838         vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7839
7840         if ( e->IsQuadratic() ) {
7841           const SMDS_VtkFace* F =
7842             dynamic_cast<const SMDS_VtkFace*>(e);
7843           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7844           // use special nodes iterator
7845           SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7846           while( anIter->more() ) {
7847             nodes[ iNode++ ] = cast2Node(anIter->next());
7848           }
7849         }
7850         else {
7851           SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7852           while ( nIt->more() )
7853             nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7854         }
7855         nodes[ iNode ] = nodes[ 0 ];
7856         // check 2 links
7857         for ( iNode = 0; iNode < nbNodes; iNode++ )
7858           if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7859                (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7860               ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7861           {
7862             nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7863             curElemList.push_back( e );
7864           }
7865       }
7866     }
7867     // analyse the found
7868
7869     int nbNewBorders = curElemList.size();
7870     if ( nbNewBorders == 0 ) {
7871       // no free border furthermore
7872       return !needTheLast;
7873     }
7874     else if ( nbNewBorders == 1 ) {
7875       // one more element found
7876       nIgnore = nStart;
7877       nStart = nStartList.front();
7878       curElem = curElemList.front();
7879       theFaces.push_back( curElem );
7880       theNodes.push_back( nStart );
7881     }
7882     else {
7883       // several continuations found
7884       list< const SMDS_MeshElement* >::iterator curElemIt;
7885       list< const SMDS_MeshNode* >::iterator nStartIt;
7886       // check if one of them reached the last node
7887       if ( needTheLast ) {
7888         for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7889              curElemIt!= curElemList.end();
7890              curElemIt++, nStartIt++ )
7891           if ( *nStartIt == theLastNode ) {
7892             theFaces.push_back( *curElemIt );
7893             theNodes.push_back( *nStartIt );
7894             return true;
7895           }
7896       }
7897       // find the best free border by the continuations
7898       list<const SMDS_MeshNode*>    contNodes[ 2 ], *cNL;
7899       list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7900       for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7901            curElemIt!= curElemList.end();
7902            curElemIt++, nStartIt++ )
7903       {
7904         cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7905         cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7906         // find one more free border
7907         if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7908           cNL->clear();
7909           cFL->clear();
7910         }
7911         else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7912           // choice: clear a worse one
7913           int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7914           int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7915           contNodes[ iWorse ].clear();
7916           contFaces[ iWorse ].clear();
7917         }
7918       }
7919       if ( contNodes[0].empty() && contNodes[1].empty() )
7920         return false;
7921
7922       // push_back the best free border
7923       cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7924       cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7925       theNodes.pop_back(); // remove nIgnore
7926       theNodes.pop_back(); // remove nStart
7927       theFaces.pop_back(); // remove curElem
7928       list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7929       list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7930       for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7931       for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7932       return true;
7933
7934     } // several continuations found
7935   } // while ( nStart != theLastNode )
7936
7937   return true;
7938 }
7939
7940 //=======================================================================
7941 //function : CheckFreeBorderNodes
7942 //purpose  : Return true if the tree nodes are on a free border
7943 //=======================================================================
7944
7945 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7946                                             const SMDS_MeshNode* theNode2,
7947                                             const SMDS_MeshNode* theNode3)
7948 {
7949   list< const SMDS_MeshNode* > nodes;
7950   list< const SMDS_MeshElement* > faces;
7951   return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7952 }
7953
7954 //=======================================================================
7955 //function : SewFreeBorder
7956 //purpose  :
7957 //warning  : for border-to-side sewing theSideSecondNode is considered as
7958 //           the last side node and theSideThirdNode is not used
7959 //=======================================================================
7960
7961 SMESH_MeshEditor::Sew_Error
7962 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7963                                  const SMDS_MeshNode* theBordSecondNode,
7964                                  const SMDS_MeshNode* theBordLastNode,
7965                                  const SMDS_MeshNode* theSideFirstNode,
7966                                  const SMDS_MeshNode* theSideSecondNode,
7967                                  const SMDS_MeshNode* theSideThirdNode,
7968                                  const bool           theSideIsFreeBorder,
7969                                  const bool           toCreatePolygons,
7970                                  const bool           toCreatePolyedrs)
7971 {
7972   ClearLastCreated();
7973
7974   Sew_Error aResult = SEW_OK;
7975
7976   // ====================================
7977   //    find side nodes and elements
7978   // ====================================
7979
7980   list< const SMDS_MeshNode* >    nSide[ 2 ];
7981   list< const SMDS_MeshElement* > eSide[ 2 ];
7982   list< const SMDS_MeshNode* >::iterator    nIt[ 2 ];
7983   list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7984
7985   // Free border 1
7986   // --------------
7987   if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7988                       nSide[0], eSide[0])) {
7989     MESSAGE(" Free Border 1 not found " );
7990     aResult = SEW_BORDER1_NOT_FOUND;
7991   }
7992   if (theSideIsFreeBorder) {
7993     // Free border 2
7994     // --------------
7995     if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7996                         nSide[1], eSide[1])) {
7997       MESSAGE(" Free Border 2 not found " );
7998       aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7999     }
8000   }
8001   if ( aResult != SEW_OK )
8002     return aResult;
8003
8004   if (!theSideIsFreeBorder) {
8005     // Side 2
8006     // --------------
8007
8008     // -------------------------------------------------------------------------
8009     // Algo:
8010     // 1. If nodes to merge are not coincident, move nodes of the free border
8011     //    from the coord sys defined by the direction from the first to last
8012     //    nodes of the border to the correspondent sys of the side 2
8013     // 2. On the side 2, find the links most co-directed with the correspondent
8014     //    links of the free border
8015     // -------------------------------------------------------------------------
8016
8017     // 1. Since sewing may break if there are volumes to split on the side 2,
8018     //    we wont move nodes but just compute new coordinates for them
8019     typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8020     TNodeXYZMap nBordXYZ;
8021     list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8022     list< const SMDS_MeshNode* >::iterator nBordIt;
8023
8024     gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8025     gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8026     gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8027     gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8028     double tol2 = 1.e-8;
8029     gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8030     if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8031       // Need node movement.
8032
8033       // find X and Z axes to create trsf
8034       gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8035       gp_Vec X = Zs ^ Zb;
8036       if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8037         // Zb || Zs
8038         X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8039
8040       // coord systems
8041       gp_Ax3 toBordAx( Pb1, Zb, X );
8042       gp_Ax3 fromSideAx( Ps1, Zs, X );
8043       gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8044       // set trsf
8045       gp_Trsf toBordSys, fromSide2Sys;
8046       toBordSys.SetTransformation( toBordAx );
8047       fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8048       fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8049
8050       // move
8051       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8052         const SMDS_MeshNode* n = *nBordIt;
8053         gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8054         toBordSys.Transforms( xyz );
8055         fromSide2Sys.Transforms( xyz );
8056         nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8057       }
8058     }
8059     else {
8060       // just insert nodes XYZ in the nBordXYZ map
8061       for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8062         const SMDS_MeshNode* n = *nBordIt;
8063         nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8064       }
8065     }
8066
8067     // 2. On the side 2, find the links most co-directed with the correspondent
8068     //    links of the free border
8069
8070     list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8071     list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8072     sideNodes.push_back( theSideFirstNode );
8073
8074     bool hasVolumes = false;
8075     LinkID_Gen aLinkID_Gen( GetMeshDS() );
8076     set<long> foundSideLinkIDs, checkedLinkIDs;
8077     SMDS_VolumeTool volume;
8078     //const SMDS_MeshNode* faceNodes[ 4 ];
8079
8080     const SMDS_MeshNode*    sideNode;
8081     const SMDS_MeshElement* sideElem  = 0;
8082     const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8083     const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8084     nBordIt = bordNodes.begin();
8085     nBordIt++;
8086     // border node position and border link direction to compare with
8087     gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8088     gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8089     // choose next side node by link direction or by closeness to
8090     // the current border node:
8091     bool searchByDir = ( *nBordIt != theBordLastNode );
8092     do {
8093       // find the next node on the Side 2
8094       sideNode = 0;
8095       double maxDot = -DBL_MAX, minDist = DBL_MAX;
8096       long linkID;
8097       checkedLinkIDs.clear();
8098       gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8099
8100       // loop on inverse elements of current node (prevSideNode) on the Side 2
8101       SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8102       while ( invElemIt->more() )
8103       {
8104         const SMDS_MeshElement* elem = invElemIt->next();
8105         // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8106         int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8107         vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8108         bool isVolume = volume.Set( elem );
8109         const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8110         if ( isVolume ) // --volume
8111           hasVolumes = true;
8112         else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8113           // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8114           if(elem->IsQuadratic()) {
8115             const SMDS_VtkFace* F =
8116               dynamic_cast<const SMDS_VtkFace*>(elem);
8117             if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8118             // use special nodes iterator
8119             SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8120             while( anIter->more() ) {
8121               nodes[ iNode ] = cast2Node(anIter->next());
8122               if ( nodes[ iNode++ ] == prevSideNode )
8123                 iPrevNode = iNode - 1;
8124             }
8125           }
8126           else {
8127             SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8128             while ( nIt->more() ) {
8129               nodes[ iNode ] = cast2Node( nIt->next() );
8130               if ( nodes[ iNode++ ] == prevSideNode )
8131                 iPrevNode = iNode - 1;
8132             }
8133           }
8134           // there are 2 links to check
8135           nbNodes = 2;
8136         }
8137         else // --edge
8138           continue;
8139         // loop on links, to be precise, on the second node of links
8140         for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8141           const SMDS_MeshNode* n = nodes[ iNode ];
8142           if ( isVolume ) {
8143             if ( !volume.IsLinked( n, prevSideNode ))
8144               continue;
8145           }
8146           else {
8147             if ( iNode ) // a node before prevSideNode
8148               n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8149             else         // a node after prevSideNode
8150               n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8151           }
8152           // check if this link was already used
8153           long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8154           bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8155           if (!isJustChecked &&
8156               foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8157           {
8158             // test a link geometrically
8159             gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8160             bool linkIsBetter = false;
8161             double dot = 0.0, dist = 0.0;
8162             if ( searchByDir ) { // choose most co-directed link
8163               dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8164               linkIsBetter = ( dot > maxDot );
8165             }
8166             else { // choose link with the node closest to bordPos
8167               dist = ( nextXYZ - bordPos ).SquareModulus();
8168               linkIsBetter = ( dist < minDist );
8169             }
8170             if ( linkIsBetter ) {
8171               maxDot = dot;
8172               minDist = dist;
8173               linkID = iLink;
8174               sideNode = n;
8175               sideElem = elem;
8176             }
8177           }
8178         }
8179       } // loop on inverse elements of prevSideNode
8180
8181       if ( !sideNode ) {
8182         MESSAGE(" Cant find path by links of the Side 2 ");
8183         return SEW_BAD_SIDE_NODES;
8184       }
8185       sideNodes.push_back( sideNode );
8186       sideElems.push_back( sideElem );
8187       foundSideLinkIDs.insert ( linkID );
8188       prevSideNode = sideNode;
8189
8190       if ( *nBordIt == theBordLastNode )
8191         searchByDir = false;
8192       else {
8193         // find the next border link to compare with
8194         gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8195         searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8196         // move to next border node if sideNode is before forward border node (bordPos)
8197         while ( *nBordIt != theBordLastNode && !searchByDir ) {
8198           prevBordNode = *nBordIt;
8199           nBordIt++;
8200           bordPos = nBordXYZ[ *nBordIt ];
8201           bordDir = bordPos - nBordXYZ[ prevBordNode ];
8202           searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8203         }
8204       }
8205     }
8206     while ( sideNode != theSideSecondNode );
8207
8208     if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8209       MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8210       return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8211     }
8212   } // end nodes search on the side 2
8213
8214   // ============================
8215   // sew the border to the side 2
8216   // ============================
8217
8218   int nbNodes[]  = { (int)nSide[0].size(), (int)nSide[1].size() };
8219   int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8220
8221   bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8222   if ( toMergeConformal && toCreatePolygons )
8223   {
8224     // do not merge quadrangles if polygons are OK (IPAL0052824)
8225     eIt[0] = eSide[0].begin();
8226     eIt[1] = eSide[1].begin();
8227     bool allQuads[2] = { true, true };
8228     for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8229       for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8230         allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8231     }
8232     toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8233   }
8234
8235   TListOfListOfNodes nodeGroupsToMerge;
8236   if (( toMergeConformal ) ||
8237       ( theSideIsFreeBorder && !theSideThirdNode )) {
8238
8239     // all nodes are to be merged
8240
8241     for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8242          nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8243          nIt[0]++, nIt[1]++ )
8244     {
8245       nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8246       nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8247       nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8248     }
8249   }
8250   else {
8251
8252     // insert new nodes into the border and the side to get equal nb of segments
8253
8254     // get normalized parameters of nodes on the borders
8255     vector< double > param[ 2 ];
8256     param[0].resize( maxNbNodes );
8257     param[1].resize( maxNbNodes );
8258     int iNode, iBord;
8259     for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8260       list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8261       list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8262       const SMDS_MeshNode* nPrev = *nIt;
8263       double bordLength = 0;
8264       for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8265         const SMDS_MeshNode* nCur = *nIt;
8266         gp_XYZ segment (nCur->X() - nPrev->X(),
8267                         nCur->Y() - nPrev->Y(),
8268                         nCur->Z() - nPrev->Z());
8269         double segmentLen = segment.Modulus();
8270         bordLength += segmentLen;
8271         param[ iBord ][ iNode ] = bordLength;
8272         nPrev = nCur;
8273       }
8274       // normalize within [0,1]
8275       for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8276         param[ iBord ][ iNode ] /= bordLength;
8277       }
8278     }
8279
8280     // loop on border segments
8281     const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8282     int i[ 2 ] = { 0, 0 };
8283     nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8284     nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8285
8286     TElemOfNodeListMap insertMap;
8287     TElemOfNodeListMap::iterator insertMapIt;
8288     // insertMap is
8289     // key:   elem to insert nodes into
8290     // value: 2 nodes to insert between + nodes to be inserted
8291     do {
8292       bool next[ 2 ] = { false, false };
8293
8294       // find min adjacent segment length after sewing
8295       double nextParam = 10., prevParam = 0;
8296       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8297         if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8298           nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8299         if ( i[ iBord ] > 0 )
8300           prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8301       }
8302       double  minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8303       double  maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8304       double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8305
8306       // choose to insert or to merge nodes
8307       double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8308       if ( Abs( du ) <= minSegLen * 0.2 ) {
8309         // merge
8310         // ------
8311         nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8312         const SMDS_MeshNode* n0 = *nIt[0];
8313         const SMDS_MeshNode* n1 = *nIt[1];
8314         nodeGroupsToMerge.back().push_back( n1 );
8315         nodeGroupsToMerge.back().push_back( n0 );
8316         // position of node of the border changes due to merge
8317         param[ 0 ][ i[0] ] += du;
8318         // move n1 for the sake of elem shape evaluation during insertion.
8319         // n1 will be removed by MergeNodes() anyway
8320         const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8321         next[0] = next[1] = true;
8322       }
8323       else {
8324         // insert
8325         // ------
8326         int intoBord = ( du < 0 ) ? 0 : 1;
8327         const SMDS_MeshElement* elem = *eIt [ intoBord ];
8328         const SMDS_MeshNode*    n1   = nPrev[ intoBord ];
8329         const SMDS_MeshNode*    n2   = *nIt [ intoBord ];
8330         const SMDS_MeshNode*    nIns = *nIt [ 1 - intoBord ];
8331         if ( intoBord == 1 ) {
8332           // move node of the border to be on a link of elem of the side
8333           gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8334           gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8335           double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8336           gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8337           GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8338         }
8339         insertMapIt = insertMap.find( elem );
8340         bool  notFound = ( insertMapIt == insertMap.end() );
8341         bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8342         if ( otherLink ) {
8343           // insert into another link of the same element:
8344           // 1. perform insertion into the other link of the elem
8345           list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8346           const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8347           const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8348           InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8349           // 2. perform insertion into the link of adjacent faces
8350           while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8351             InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8352           }
8353           while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8354             InsertNodesIntoLink( seg, n12, n22, nodeList );
8355           }
8356           if (toCreatePolyedrs) {
8357             // perform insertion into the links of adjacent volumes
8358             UpdateVolumes(n12, n22, nodeList);
8359           }
8360           // 3. find an element appeared on n1 and n2 after the insertion
8361           insertMap.erase( elem );
8362           elem = findAdjacentFace( n1, n2, 0 );
8363         }
8364         if ( notFound || otherLink ) {
8365           // add element and nodes of the side into the insertMap
8366           insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8367           (*insertMapIt).second.push_back( n1 );
8368           (*insertMapIt).second.push_back( n2 );
8369         }
8370         // add node to be inserted into elem
8371         (*insertMapIt).second.push_back( nIns );
8372         next[ 1 - intoBord ] = true;
8373       }
8374
8375       // go to the next segment
8376       for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8377         if ( next[ iBord ] ) {
8378           if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8379             eIt[ iBord ]++;
8380           nPrev[ iBord ] = *nIt[ iBord ];
8381           nIt[ iBord ]++; i[ iBord ]++;
8382         }
8383       }
8384     }
8385     while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8386
8387     // perform insertion of nodes into elements
8388
8389     for (insertMapIt = insertMap.begin();
8390          insertMapIt != insertMap.end();
8391          insertMapIt++ )
8392     {
8393       const SMDS_MeshElement* elem = (*insertMapIt).first;
8394       list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8395       const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8396       const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8397
8398       InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8399
8400       while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8401         InsertNodesIntoLink( seg, n1, n2, nodeList );
8402       }
8403
8404       if ( !theSideIsFreeBorder ) {
8405         // look for and insert nodes into the faces adjacent to elem
8406         while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8407           InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8408         }
8409       }
8410       if (toCreatePolyedrs) {
8411         // perform insertion into the links of adjacent volumes
8412         UpdateVolumes(n1, n2, nodeList);
8413       }
8414     }
8415   } // end: insert new nodes
8416
8417   MergeNodes ( nodeGroupsToMerge );
8418
8419
8420   // Remove coincident segments
8421
8422   // get new segments
8423   TIDSortedElemSet segments;
8424   SMESH_SequenceOfElemPtr newFaces;
8425   for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8426   {
8427     if ( !myLastCreatedElems[i] ) continue;
8428     if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8429       segments.insert( segments.end(), myLastCreatedElems[i] );
8430     else
8431       newFaces.push_back( myLastCreatedElems[i] );
8432   }
8433   // get segments adjacent to merged nodes
8434   TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8435   for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8436   {
8437     const list<const SMDS_MeshNode*>& nodes = *groupIt;
8438     SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8439     while ( segIt->more() )
8440       segments.insert( segIt->next() );
8441   }
8442
8443   // find coincident
8444   TListOfListOfElementsID equalGroups;
8445   if ( !segments.empty() )
8446     FindEqualElements( segments, equalGroups );
8447   if ( !equalGroups.empty() )
8448   {
8449     // remove from segments those that will be removed
8450     TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8451     for ( ; itGroups != equalGroups.end(); ++itGroups )
8452     {
8453       list< int >& group = *itGroups;
8454       list< int >::iterator id = group.begin();
8455       for ( ++id; id != group.end(); ++id )
8456         if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8457           segments.erase( seg );
8458     }
8459     // remove equal segments
8460     MergeElements( equalGroups );
8461
8462     // restore myLastCreatedElems
8463     myLastCreatedElems = newFaces;
8464     TIDSortedElemSet::iterator seg = segments.begin();
8465     for ( ; seg != segments.end(); ++seg )
8466       myLastCreatedElems.push_back( *seg );
8467   }
8468
8469   return aResult;
8470 }
8471
8472 //=======================================================================
8473 //function : InsertNodesIntoLink
8474 //purpose  : insert theNodesToInsert into theElement between theBetweenNode1
8475 //           and theBetweenNode2 and split theElement
8476 //=======================================================================
8477
8478 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement*     theElement,
8479                                            const SMDS_MeshNode*        theBetweenNode1,
8480                                            const SMDS_MeshNode*        theBetweenNode2,
8481                                            list<const SMDS_MeshNode*>& theNodesToInsert,
8482                                            const bool                  toCreatePoly)
8483 {
8484   if ( !theElement ) return;
8485
8486   SMESHDS_Mesh *aMesh = GetMeshDS();
8487   vector<const SMDS_MeshElement*> newElems;
8488
8489   if ( theElement->GetType() == SMDSAbs_Edge )
8490   {
8491     theNodesToInsert.push_front( theBetweenNode1 );
8492     theNodesToInsert.push_back ( theBetweenNode2 );
8493     list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8494     const SMDS_MeshNode* n1 = *n;
8495     for ( ++n; n != theNodesToInsert.end(); ++n )
8496     {
8497       const SMDS_MeshNode* n2 = *n;
8498       if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8499         AddToSameGroups( seg, theElement, aMesh );
8500       else
8501         newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8502       n1 = n2;
8503     }
8504     theNodesToInsert.pop_front();
8505     theNodesToInsert.pop_back();
8506
8507     if ( theElement->IsQuadratic() ) // add a not split part
8508     {
8509       vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8510                                           theElement->end_nodes() );
8511       int iOther = 0, nbN = nodes.size();
8512       for ( ; iOther < nbN; ++iOther )
8513         if ( nodes[iOther] != theBetweenNode1 &&
8514              nodes[iOther] != theBetweenNode2 )
8515           break;
8516       if      ( iOther == 0 )
8517       {
8518         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8519           AddToSameGroups( seg, theElement, aMesh );
8520         else
8521           newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8522       }
8523       else if ( iOther == 2 )
8524       {
8525         if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8526           AddToSameGroups( seg, theElement, aMesh );
8527         else
8528           newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8529       }
8530     }
8531     // treat new elements
8532     for ( size_t i = 0; i < newElems.size(); ++i )
8533       if ( newElems[i] )
8534       {
8535         aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8536         myLastCreatedElems.push_back( newElems[i] );
8537       }
8538     ReplaceElemInGroups( theElement, newElems, aMesh );
8539     aMesh->RemoveElement( theElement );
8540     return;
8541
8542   } // if ( theElement->GetType() == SMDSAbs_Edge )
8543
8544   const SMDS_MeshElement* theFace = theElement;
8545   if ( theFace->GetType() != SMDSAbs_Face ) return;
8546
8547   // find indices of 2 link nodes and of the rest nodes
8548   int iNode = 0, il1, il2, i3, i4;
8549   il1 = il2 = i3 = i4 = -1;
8550   vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8551
8552   SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8553   while ( nodeIt->more() ) {
8554     const SMDS_MeshNode* n = nodeIt->next();
8555     if ( n == theBetweenNode1 )
8556       il1 = iNode;
8557     else if ( n == theBetweenNode2 )
8558       il2 = iNode;
8559     else if ( i3 < 0 )
8560       i3 = iNode;
8561     else
8562       i4 = iNode;
8563     nodes[ iNode++ ] = n;
8564   }
8565   if ( il1 < 0 || il2 < 0 || i3 < 0 )
8566     return ;
8567
8568   // arrange link nodes to go one after another regarding the face orientation
8569   bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8570   list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8571   if ( reverse ) {
8572     iNode = il1;
8573     il1 = il2;
8574     il2 = iNode;
8575     aNodesToInsert.reverse();
8576   }
8577   // check that not link nodes of a quadrangles are in good order
8578   int nbFaceNodes = theFace->NbNodes();
8579   if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8580     iNode = i3;
8581     i3 = i4;
8582     i4 = iNode;
8583   }
8584
8585   if (toCreatePoly || theFace->IsPoly()) {
8586
8587     iNode = 0;
8588     vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8589
8590     // add nodes of face up to first node of link
8591     bool isFLN = false;
8592
8593     if ( theFace->IsQuadratic() ) {
8594       const SMDS_VtkFace* F = 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     // make a new face
8637     newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8638   }
8639
8640   else if ( !theFace->IsQuadratic() )
8641   {
8642     // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8643     int nbLinkNodes = 2 + aNodesToInsert.size();
8644     //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8645     vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8646     linkNodes[ 0 ] = nodes[ il1 ];
8647     linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8648     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8649     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8650       linkNodes[ iNode++ ] = *nIt;
8651     }
8652     // decide how to split a quadrangle: compare possible variants
8653     // and choose which of splits to be a quadrangle
8654     int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8655     if ( nbFaceNodes == 3 ) {
8656       iBestQuad = nbSplits;
8657       i4 = i3;
8658     }
8659     else if ( nbFaceNodes == 4 ) {
8660       SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8661       double aBestRate = DBL_MAX;
8662       for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8663         i1 = 0; i2 = 1;
8664         double aBadRate = 0;
8665         // evaluate elements quality
8666         for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8667           if ( iSplit == iQuad ) {
8668             SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8669                                    linkNodes[ i2++ ],
8670                                    nodes[ i3 ],
8671                                    nodes[ i4 ]);
8672             aBadRate += getBadRate( &quad, aCrit );
8673           }
8674           else {
8675             SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8676                                    linkNodes[ i2++ ],
8677                                    nodes[ iSplit < iQuad ? i4 : i3 ]);
8678             aBadRate += getBadRate( &tria, aCrit );
8679           }
8680         }
8681         // choice
8682         if ( aBadRate < aBestRate ) {
8683           iBestQuad = iQuad;
8684           aBestRate = aBadRate;
8685         }
8686       }
8687     }
8688
8689     // create new elements
8690     i1 = 0; i2 = 1;
8691     for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8692     {
8693       if ( iSplit == iBestQuad )
8694         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8695                                             linkNodes[ i2++ ],
8696                                             nodes[ i3 ],
8697                                             nodes[ i4 ]));
8698       else
8699         newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8700                                             linkNodes[ i2++ ],
8701                                             nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8702     }
8703
8704     const SMDS_MeshNode* newNodes[ 4 ];
8705     newNodes[ 0 ] = linkNodes[ i1 ];
8706     newNodes[ 1 ] = linkNodes[ i2 ];
8707     newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8708     newNodes[ 3 ] = nodes[ i4 ];
8709     if (iSplit == iBestQuad)
8710       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8711     else
8712       newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8713
8714   } // end if(!theFace->IsQuadratic())
8715
8716   else { // theFace is quadratic
8717     // we have to split theFace on simple triangles and one simple quadrangle
8718     int tmp = il1/2;
8719     int nbshift = tmp*2;
8720     // shift nodes in nodes[] by nbshift
8721     int i,j;
8722     for(i=0; i<nbshift; i++) {
8723       const SMDS_MeshNode* n = nodes[0];
8724       for(j=0; j<nbFaceNodes-1; j++) {
8725         nodes[j] = nodes[j+1];
8726       }
8727       nodes[nbFaceNodes-1] = n;
8728     }
8729     il1 = il1 - nbshift;
8730     // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8731     //   n0      n1     n2    n0      n1     n2
8732     //     +-----+-----+        +-----+-----+
8733     //      \         /         |           |
8734     //       \       /          |           |
8735     //      n5+     +n3       n7+           +n3
8736     //         \   /            |           |
8737     //          \ /             |           |
8738     //           +              +-----+-----+
8739     //           n4           n6      n5     n4
8740
8741     // create new elements
8742     int n1,n2,n3;
8743     if ( nbFaceNodes == 6 ) { // quadratic triangle
8744       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8745       if ( theFace->IsMediumNode(nodes[il1]) ) {
8746         // create quadrangle
8747         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8748         n1 = 1;
8749         n2 = 2;
8750         n3 = 3;
8751       }
8752       else {
8753         // create quadrangle
8754         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8755         n1 = 0;
8756         n2 = 1;
8757         n3 = 5;
8758       }
8759     }
8760     else { // nbFaceNodes==8 - quadratic quadrangle
8761       newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8762       newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8763       newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8764       if ( theFace->IsMediumNode( nodes[ il1 ])) {
8765         // create quadrangle
8766         newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8767         n1 = 1;
8768         n2 = 2;
8769         n3 = 3;
8770       }
8771       else {
8772         // create quadrangle
8773         newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8774         n1 = 0;
8775         n2 = 1;
8776         n3 = 7;
8777       }
8778     }
8779     // create needed triangles using n1,n2,n3 and inserted nodes
8780     int nbn = 2 + aNodesToInsert.size();
8781     vector<const SMDS_MeshNode*> aNodes(nbn);
8782     aNodes[0    ] = nodes[n1];
8783     aNodes[nbn-1] = nodes[n2];
8784     list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8785     for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8786       aNodes[iNode++] = *nIt;
8787     }
8788     for ( i = 1; i < nbn; i++ )
8789       newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8790   }
8791
8792   // remove the old face
8793   for ( size_t i = 0; i < newElems.size(); ++i )
8794     if ( newElems[i] )
8795     {
8796       aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8797       myLastCreatedElems.push_back( newElems[i] );
8798     }
8799   ReplaceElemInGroups( theFace, newElems, aMesh );
8800   aMesh->RemoveElement(theFace);
8801
8802 } // InsertNodesIntoLink()
8803
8804 //=======================================================================
8805 //function : UpdateVolumes
8806 //purpose  :
8807 //=======================================================================
8808
8809 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode*        theBetweenNode1,
8810                                       const SMDS_MeshNode*        theBetweenNode2,
8811                                       list<const SMDS_MeshNode*>& theNodesToInsert)
8812 {
8813   ClearLastCreated();
8814
8815   SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8816   while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8817     const SMDS_MeshElement* elem = invElemIt->next();
8818
8819     // check, if current volume has link theBetweenNode1 - theBetweenNode2
8820     SMDS_VolumeTool aVolume (elem);
8821     if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8822       continue;
8823
8824     // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8825     int iface, nbFaces = aVolume.NbFaces();
8826     vector<const SMDS_MeshNode *> poly_nodes;
8827     vector<int> quantities (nbFaces);
8828
8829     for (iface = 0; iface < nbFaces; iface++) {
8830       int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8831       // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8832       const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8833
8834       for (int inode = 0; inode < nbFaceNodes; inode++) {
8835         poly_nodes.push_back(faceNodes[inode]);
8836
8837         if (nbInserted == 0) {
8838           if (faceNodes[inode] == theBetweenNode1) {
8839             if (faceNodes[inode + 1] == theBetweenNode2) {
8840               nbInserted = theNodesToInsert.size();
8841
8842               // add nodes to insert
8843               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8844               for (; nIt != theNodesToInsert.end(); nIt++) {
8845                 poly_nodes.push_back(*nIt);
8846               }
8847             }
8848           }
8849           else if (faceNodes[inode] == theBetweenNode2) {
8850             if (faceNodes[inode + 1] == theBetweenNode1) {
8851               nbInserted = theNodesToInsert.size();
8852
8853               // add nodes to insert in reversed order
8854               list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8855               nIt--;
8856               for (; nIt != theNodesToInsert.begin(); nIt--) {
8857                 poly_nodes.push_back(*nIt);
8858               }
8859               poly_nodes.push_back(*nIt);
8860             }
8861           }
8862           else {
8863           }
8864         }
8865       }
8866       quantities[iface] = nbFaceNodes + nbInserted;
8867     }
8868
8869     // Replace the volume
8870     SMESHDS_Mesh *aMesh = GetMeshDS();
8871
8872     if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8873     {
8874       aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8875       myLastCreatedElems.push_back( newElem );
8876       ReplaceElemInGroups( elem, newElem, aMesh );
8877     }
8878     aMesh->RemoveElement( elem );
8879   }
8880 }
8881
8882 namespace
8883 {
8884   //================================================================================
8885   /*!
8886    * \brief Transform any volume into data of SMDSEntity_Polyhedra
8887    */
8888   //================================================================================
8889
8890   void volumeToPolyhedron( const SMDS_MeshElement*         elem,
8891                            vector<const SMDS_MeshNode *> & nodes,
8892                            vector<int> &                   nbNodeInFaces )
8893   {
8894     nodes.clear();
8895     nbNodeInFaces.clear();
8896     SMDS_VolumeTool vTool ( elem );
8897     for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8898     {
8899       const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8900       nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8901       nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8902     }
8903   }
8904 }
8905
8906 //=======================================================================
8907 /*!
8908  * \brief Convert elements contained in a sub-mesh to quadratic
8909  * \return int - nb of checked elements
8910  */
8911 //=======================================================================
8912
8913 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh *   theSm,
8914                                              SMESH_MesherHelper& theHelper,
8915                                              const bool          theForce3d)
8916 {
8917   int nbElem = 0;
8918   if( !theSm ) return nbElem;
8919
8920   vector<int> nbNodeInFaces;
8921   vector<const SMDS_MeshNode *> nodes;
8922   SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8923   while(ElemItr->more())
8924   {
8925     nbElem++;
8926     const SMDS_MeshElement* elem = ElemItr->next();
8927     if( !elem ) continue;
8928
8929     // analyse a necessity of conversion
8930     const SMDSAbs_ElementType aType = elem->GetType();
8931     if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8932       continue;
8933     const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8934     bool hasCentralNodes = false;
8935     if ( elem->IsQuadratic() )
8936     {
8937       bool alreadyOK;
8938       switch ( aGeomType ) {
8939       case SMDSEntity_Quad_Triangle:
8940       case SMDSEntity_Quad_Quadrangle:
8941       case SMDSEntity_Quad_Hexa:
8942         alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8943
8944       case SMDSEntity_BiQuad_Triangle:
8945       case SMDSEntity_BiQuad_Quadrangle:
8946       case SMDSEntity_TriQuad_Hexa:
8947         alreadyOK = theHelper.GetIsBiQuadratic();
8948         hasCentralNodes = true;
8949         break;
8950       default:
8951         alreadyOK = true;
8952       }
8953       // take into account already present modium nodes
8954       switch ( aType ) {
8955       case SMDSAbs_Volume:
8956         theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8957       case SMDSAbs_Face:
8958         theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8959       case SMDSAbs_Edge:
8960         theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8961       default:;
8962       }
8963       if ( alreadyOK )
8964         continue;
8965     }
8966     // get elem data needed to re-create it
8967     //
8968     const int id      = elem->GetID();
8969     const int nbNodes = elem->NbCornerNodes();
8970     nodes.assign(elem->begin_nodes(), elem->end_nodes());
8971     if ( aGeomType == SMDSEntity_Polyhedra )
8972       nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8973     else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8974       volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8975
8976     // remove a linear element
8977     GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8978
8979     // remove central nodes of biquadratic elements (biquad->quad convertion)
8980     if ( hasCentralNodes )
8981       for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8982         if ( nodes[i]->NbInverseElements() == 0 )
8983           GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8984
8985     const SMDS_MeshElement* NewElem = 0;
8986
8987     switch( aType )
8988     {
8989     case SMDSAbs_Edge :
8990     {
8991       NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8992       break;
8993     }
8994     case SMDSAbs_Face :
8995     {
8996       switch(nbNodes)
8997       {
8998       case 3:
8999         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9000         break;
9001       case 4:
9002         NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9003         break;
9004       default:
9005         NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9006       }
9007       break;
9008     }
9009     case SMDSAbs_Volume :
9010     {
9011       switch( aGeomType )
9012       {
9013       case SMDSEntity_Tetra:
9014         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9015         break;
9016       case SMDSEntity_Pyramid:
9017         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9018         break;
9019       case SMDSEntity_Penta:
9020         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9021         break;
9022       case SMDSEntity_Hexa:
9023       case SMDSEntity_Quad_Hexa:
9024       case SMDSEntity_TriQuad_Hexa:
9025         NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9026                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9027         break;
9028       case SMDSEntity_Hexagonal_Prism:
9029       default:
9030         NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9031       }
9032       break;
9033     }
9034     default :
9035       continue;
9036     }
9037     ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9038     if( NewElem && NewElem->getshapeId() < 1 )
9039       theSm->AddElement( NewElem );
9040   }
9041   return nbElem;
9042 }
9043 //=======================================================================
9044 //function : ConvertToQuadratic
9045 //purpose  :
9046 //=======================================================================
9047
9048 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9049 {
9050   SMESHDS_Mesh* meshDS = GetMeshDS();
9051
9052   SMESH_MesherHelper aHelper(*myMesh);
9053
9054   aHelper.SetIsQuadratic( true );
9055   aHelper.SetIsBiQuadratic( theToBiQuad );
9056   aHelper.SetElementsOnShape(true);
9057   aHelper.ToFixNodeParameters( true );
9058
9059   // convert elements assigned to sub-meshes
9060   int nbCheckedElems = 0;
9061   if ( myMesh->HasShapeToMesh() )
9062   {
9063     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9064     {
9065       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9066       while ( smIt->more() ) {
9067         SMESH_subMesh* sm = smIt->next();
9068         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9069           aHelper.SetSubShape( sm->GetSubShape() );
9070           nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9071         }
9072       }
9073     }
9074   }
9075
9076   // convert elements NOT assigned to sub-meshes
9077   int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9078   if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9079   {
9080     aHelper.SetElementsOnShape(false);
9081     SMESHDS_SubMesh *smDS = 0;
9082
9083     // convert edges
9084     SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9085     while( aEdgeItr->more() )
9086     {
9087       const SMDS_MeshEdge* edge = aEdgeItr->next();
9088       if ( !edge->IsQuadratic() )
9089       {
9090         int                  id = edge->GetID();
9091         const SMDS_MeshNode* n1 = edge->GetNode(0);
9092         const SMDS_MeshNode* n2 = edge->GetNode(1);
9093
9094         meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9095
9096         const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9097         ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9098       }
9099       else
9100       {
9101         aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9102       }
9103     }
9104
9105     // convert faces
9106     SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9107     while( aFaceItr->more() )
9108     {
9109       const SMDS_MeshFace* face = aFaceItr->next();
9110       if ( !face ) continue;
9111       
9112       const SMDSAbs_EntityType type = face->GetEntityType();
9113       bool alreadyOK;
9114       switch( type )
9115       {
9116       case SMDSEntity_Quad_Triangle:
9117       case SMDSEntity_Quad_Quadrangle:
9118         alreadyOK = !theToBiQuad;
9119         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9120         break;
9121       case SMDSEntity_BiQuad_Triangle:
9122       case SMDSEntity_BiQuad_Quadrangle:
9123         alreadyOK = theToBiQuad;
9124         aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9125         break;
9126       default: alreadyOK = false;
9127       }
9128       if ( alreadyOK )
9129         continue;
9130
9131       const int id = face->GetID();
9132       vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9133
9134       meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9135
9136       SMDS_MeshFace * NewFace = 0;
9137       switch( type )
9138       {
9139       case SMDSEntity_Triangle:
9140       case SMDSEntity_Quad_Triangle:
9141       case SMDSEntity_BiQuad_Triangle:
9142         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9143         if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9144           GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9145         break;
9146
9147       case SMDSEntity_Quadrangle:
9148       case SMDSEntity_Quad_Quadrangle:
9149       case SMDSEntity_BiQuad_Quadrangle:
9150         NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9151         if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9152           GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9153         break;
9154
9155       default:;
9156         NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9157       }
9158       ReplaceElemInGroups( face, NewFace, GetMeshDS());
9159     }
9160
9161     // convert volumes
9162     vector<int> nbNodeInFaces;
9163     SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9164     while(aVolumeItr->more())
9165     {
9166       const SMDS_MeshVolume* volume = aVolumeItr->next();
9167       if ( !volume ) continue;
9168
9169       const SMDSAbs_EntityType type = volume->GetEntityType();
9170       if ( volume->IsQuadratic() )
9171       {
9172         bool alreadyOK;
9173         switch ( type )
9174         {
9175         case SMDSEntity_Quad_Hexa:    alreadyOK = !theToBiQuad; break;
9176         case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9177         default:                      alreadyOK = true;
9178         }
9179         if ( alreadyOK )
9180         {
9181           aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9182           continue;
9183         }
9184       }
9185       const int id = volume->GetID();
9186       vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9187       if ( type == SMDSEntity_Polyhedra )
9188         nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9189       else if ( type == SMDSEntity_Hexagonal_Prism )
9190         volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9191
9192       meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9193
9194       SMDS_MeshVolume * NewVolume = 0;
9195       switch ( type )
9196       {
9197       case SMDSEntity_Tetra:
9198         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9199         break;
9200       case SMDSEntity_Hexa:
9201       case SMDSEntity_Quad_Hexa:
9202       case SMDSEntity_TriQuad_Hexa:
9203         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9204                                       nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9205         for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9206           if ( nodes[i]->NbInverseElements() == 0 )
9207             GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9208         break;
9209       case SMDSEntity_Pyramid:
9210         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9211                                       nodes[3], nodes[4], id, theForce3d);
9212         break;
9213       case SMDSEntity_Penta:
9214         NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9215                                       nodes[3], nodes[4], nodes[5], id, theForce3d);
9216         break;
9217       case SMDSEntity_Hexagonal_Prism:
9218       default:
9219         NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9220       }
9221       ReplaceElemInGroups(volume, NewVolume, meshDS);
9222     }
9223   }
9224
9225   if ( !theForce3d )
9226   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9227     // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9228     // aHelper.FixQuadraticElements(myError);
9229     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9230   }
9231 }
9232
9233 //================================================================================
9234 /*!
9235  * \brief Makes given elements quadratic
9236  *  \param theForce3d - if true, the medium nodes will be placed in the middle of link
9237  *  \param theElements - elements to make quadratic
9238  */
9239 //================================================================================
9240
9241 void SMESH_MeshEditor::ConvertToQuadratic(const bool        theForce3d,
9242                                           TIDSortedElemSet& theElements,
9243                                           const bool        theToBiQuad)
9244 {
9245   if ( theElements.empty() ) return;
9246
9247   // we believe that all theElements are of the same type
9248   const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9249
9250   // get all nodes shared by theElements
9251   TIDSortedNodeSet allNodes;
9252   TIDSortedElemSet::iterator eIt = theElements.begin();
9253   for ( ; eIt != theElements.end(); ++eIt )
9254     allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9255
9256   // complete theElements with elements of lower dim whose all nodes are in allNodes
9257
9258   TIDSortedElemSet quadAdjacentElems    [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9259   TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9260   TIDSortedNodeSet::iterator nIt = allNodes.begin();
9261   for ( ; nIt != allNodes.end(); ++nIt )
9262   {
9263     const SMDS_MeshNode* n = *nIt;
9264     SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9265     while ( invIt->more() )
9266     {
9267       const SMDS_MeshElement*      e = invIt->next();
9268       const SMDSAbs_ElementType type = e->GetType();
9269       if ( e->IsQuadratic() )
9270       {
9271         quadAdjacentElems[ type ].insert( e );
9272
9273         bool alreadyOK;
9274         switch ( e->GetEntityType() ) {
9275         case SMDSEntity_Quad_Triangle:
9276         case SMDSEntity_Quad_Quadrangle:
9277         case SMDSEntity_Quad_Hexa:         alreadyOK = !theToBiQuad; break;
9278         case SMDSEntity_BiQuad_Triangle:
9279         case SMDSEntity_BiQuad_Quadrangle:
9280         case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; break;
9281         default:                           alreadyOK = true;
9282         }
9283         if ( alreadyOK )
9284           continue;
9285       }
9286       if ( type >= elemType )
9287         continue; // same type or more complex linear element
9288
9289       if ( !checkedAdjacentElems[ type ].insert( e ).second )
9290         continue; // e is already checked
9291
9292       // check nodes
9293       bool allIn = true;
9294       SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9295       while ( nodeIt->more() && allIn )
9296         allIn = allNodes.count( nodeIt->next() );
9297       if ( allIn )
9298         theElements.insert(e );
9299     }
9300   }
9301
9302   SMESH_MesherHelper helper(*myMesh);
9303   helper.SetIsQuadratic( true );
9304   helper.SetIsBiQuadratic( theToBiQuad );
9305
9306   // add links of quadratic adjacent elements to the helper
9307
9308   if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9309     for ( eIt  = quadAdjacentElems[SMDSAbs_Edge].begin();
9310           eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9311     {
9312       helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9313     }
9314   if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9315     for ( eIt  = quadAdjacentElems[SMDSAbs_Face].begin();
9316           eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9317     {
9318       helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9319     }
9320   if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9321     for ( eIt  = quadAdjacentElems[SMDSAbs_Volume].begin();
9322           eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9323     {
9324       helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9325     }
9326
9327   // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9328
9329   SMESHDS_Mesh*  meshDS = GetMeshDS();
9330   SMESHDS_SubMesh* smDS = 0;
9331   for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9332   {
9333     const SMDS_MeshElement* elem = *eIt;
9334
9335     bool alreadyOK;
9336     int nbCentralNodes = 0;
9337     switch ( elem->GetEntityType() ) {
9338       // linear convertible
9339     case SMDSEntity_Edge:
9340     case SMDSEntity_Triangle:
9341     case SMDSEntity_Quadrangle:
9342     case SMDSEntity_Tetra:
9343     case SMDSEntity_Pyramid:
9344     case SMDSEntity_Hexa:
9345     case SMDSEntity_Penta:             alreadyOK = false;       nbCentralNodes = 0; break;
9346       // quadratic that can become bi-quadratic
9347     case SMDSEntity_Quad_Triangle:
9348     case SMDSEntity_Quad_Quadrangle:
9349     case SMDSEntity_Quad_Hexa:         alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9350       // bi-quadratic
9351     case SMDSEntity_BiQuad_Triangle:
9352     case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9353     case SMDSEntity_TriQuad_Hexa:      alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9354       // the rest
9355     default:                           alreadyOK = true;
9356     }
9357     if ( alreadyOK ) continue;
9358
9359     const SMDSAbs_ElementType type = elem->GetType();
9360     const int                   id = elem->GetID();
9361     const int              nbNodes = elem->NbCornerNodes();
9362     vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9363
9364     helper.SetSubShape( elem->getshapeId() );
9365
9366     if ( !smDS || !smDS->Contains( elem ))
9367       smDS = meshDS->MeshElements( elem->getshapeId() );
9368     meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9369
9370     SMDS_MeshElement * newElem = 0;
9371     switch( nbNodes )
9372     {
9373     case 4: // cases for most frequently used element types go first (for optimization)
9374       if ( type == SMDSAbs_Volume )
9375         newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9376       else
9377         newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9378       break;
9379     case 8:
9380       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9381                                  nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9382       break;
9383     case 3:
9384       newElem = helper.AddFace  (nodes[0], nodes[1], nodes[2], id, theForce3d);
9385       break;
9386     case 2:
9387       newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9388       break;
9389     case 5:
9390       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9391                                  nodes[4], id, theForce3d);
9392       break;
9393     case 6:
9394       newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9395                                  nodes[4], nodes[5], id, theForce3d);
9396       break;
9397     default:;
9398     }
9399     ReplaceElemInGroups( elem, newElem, meshDS);
9400     if( newElem && smDS )
9401       smDS->AddElement( newElem );
9402
9403     // remove central nodes
9404     for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9405       if ( nodes[i]->NbInverseElements() == 0 )
9406         meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9407
9408   } // loop on theElements
9409
9410   if ( !theForce3d )
9411   { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9412     // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9413     // helper.FixQuadraticElements( myError );
9414     SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9415   }
9416 }
9417
9418 //=======================================================================
9419 /*!
9420  * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9421  * \return int - nb of checked elements
9422  */
9423 //=======================================================================
9424
9425 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh *    theSm,
9426                                      SMDS_ElemIteratorPtr theItr,
9427                                      const int            theShapeID)
9428 {
9429   int nbElem = 0;
9430   SMESHDS_Mesh* meshDS = GetMeshDS();
9431   ElemFeatures elemType;
9432   vector<const SMDS_MeshNode *> nodes;
9433
9434   while( theItr->more() )
9435   {
9436     const SMDS_MeshElement* elem = theItr->next();
9437     nbElem++;
9438     if( elem && elem->IsQuadratic())
9439     {
9440       // get elem data
9441       int nbCornerNodes = elem->NbCornerNodes();
9442       nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9443
9444       elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9445
9446       //remove a quadratic element
9447       if ( !theSm || !theSm->Contains( elem ))
9448         theSm = meshDS->MeshElements( elem->getshapeId() );
9449       meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9450
9451       // remove medium nodes
9452       for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9453         if ( nodes[i]->NbInverseElements() == 0 )
9454           meshDS->RemoveFreeNode( nodes[i], theSm );
9455
9456       // add a linear element
9457       nodes.resize( nbCornerNodes );
9458       SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9459       ReplaceElemInGroups(elem, newElem, meshDS);
9460       if( theSm && newElem )
9461         theSm->AddElement( newElem );
9462     }
9463   }
9464   return nbElem;
9465 }
9466
9467 //=======================================================================
9468 //function : ConvertFromQuadratic
9469 //purpose  :
9470 //=======================================================================
9471
9472 bool SMESH_MeshEditor::ConvertFromQuadratic()
9473 {
9474   int nbCheckedElems = 0;
9475   if ( myMesh->HasShapeToMesh() )
9476   {
9477     if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9478     {
9479       SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9480       while ( smIt->more() ) {
9481         SMESH_subMesh* sm = smIt->next();
9482         if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9483           nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9484       }
9485     }
9486   }
9487
9488   int totalNbElems =
9489     GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9490   if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9491   {
9492     SMESHDS_SubMesh *aSM = 0;
9493     removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9494   }
9495
9496   return true;
9497 }
9498
9499 namespace
9500 {
9501   //================================================================================
9502   /*!
9503    * \brief Return true if all medium nodes of the element are in the node set
9504    */
9505   //================================================================================
9506
9507   bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9508   {
9509     for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9510       if ( !nodeSet.count( elem->GetNode(i) ))
9511         return false;
9512     return true;
9513   }
9514 }
9515
9516 //================================================================================
9517 /*!
9518  * \brief Makes given elements linear
9519  */
9520 //================================================================================
9521
9522 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9523 {
9524   if ( theElements.empty() ) return;
9525
9526   // collect IDs of medium nodes of theElements; some of these nodes will be removed
9527   set<int> mediumNodeIDs;
9528   TIDSortedElemSet::iterator eIt = theElements.begin();
9529   for ( ; eIt != theElements.end(); ++eIt )
9530   {
9531     const SMDS_MeshElement* e = *eIt;
9532     for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9533       mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9534   }
9535
9536   // replace given elements by linear ones
9537   SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9538   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9539
9540   // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9541   // except those elements sharing medium nodes of quadratic element whose medium nodes
9542   // are not all in mediumNodeIDs
9543
9544   // get remaining medium nodes
9545   TIDSortedNodeSet mediumNodes;
9546   set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9547   for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9548     if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9549       mediumNodes.insert( mediumNodes.end(), n );
9550
9551   // find more quadratic elements to convert
9552   TIDSortedElemSet moreElemsToConvert;
9553   TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9554   for ( ; nIt != mediumNodes.end(); ++nIt )
9555   {
9556     SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9557     while ( invIt->more() )
9558     {
9559       const SMDS_MeshElement* e = invIt->next();
9560       if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9561       {
9562         // find a more complex element including e and
9563         // whose medium nodes are not in mediumNodes
9564         bool complexFound = false;
9565         for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9566         {
9567           SMDS_ElemIteratorPtr invIt2 =
9568             (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9569           while ( invIt2->more() )
9570           {
9571             const SMDS_MeshElement* eComplex = invIt2->next();
9572             if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9573             {
9574               int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9575               if ( nbCommonNodes == e->NbNodes())
9576               {
9577                 complexFound = true;
9578                 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9579                 break;
9580               }
9581             }
9582           }
9583         }
9584         if ( !complexFound )
9585           moreElemsToConvert.insert( e );
9586       }
9587     }
9588   }
9589   elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9590   removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9591 }
9592
9593 //=======================================================================
9594 //function : SewSideElements
9595 //purpose  :
9596 //=======================================================================
9597
9598 SMESH_MeshEditor::Sew_Error
9599 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet&    theSide1,
9600                                    TIDSortedElemSet&    theSide2,
9601                                    const SMDS_MeshNode* theFirstNode1,
9602                                    const SMDS_MeshNode* theFirstNode2,
9603                                    const SMDS_MeshNode* theSecondNode1,
9604                                    const SMDS_MeshNode* theSecondNode2)
9605 {
9606   ClearLastCreated();
9607
9608   if ( theSide1.size() != theSide2.size() )
9609     return SEW_DIFF_NB_OF_ELEMENTS;
9610
9611   Sew_Error aResult = SEW_OK;
9612   // Algo:
9613   // 1. Build set of faces representing each side
9614   // 2. Find which nodes of the side 1 to merge with ones on the side 2
9615   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9616
9617   // =======================================================================
9618   // 1. Build set of faces representing each side:
9619   // =======================================================================
9620   // a. build set of nodes belonging to faces
9621   // b. complete set of faces: find missing faces whose nodes are in set of nodes
9622   // c. create temporary faces representing side of volumes if correspondent
9623   //    face does not exist
9624
9625   SMESHDS_Mesh* aMesh = GetMeshDS();
9626   // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9627   //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9628   TIDSortedElemSet             faceSet1, faceSet2;
9629   set<const SMDS_MeshElement*> volSet1,  volSet2;
9630   set<const SMDS_MeshNode*>    nodeSet1, nodeSet2;
9631   TIDSortedElemSet             * faceSetPtr[] = { &faceSet1, &faceSet2 };
9632   set<const SMDS_MeshElement*> *  volSetPtr[] = { &volSet1,  &volSet2  };
9633   set<const SMDS_MeshNode*>    * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9634   TIDSortedElemSet             * elemSetPtr[] = { &theSide1, &theSide2 };
9635   int iSide, iFace, iNode;
9636
9637   list<const SMDS_MeshElement* > tempFaceList;
9638   for ( iSide = 0; iSide < 2; iSide++ ) {
9639     set<const SMDS_MeshNode*>    * nodeSet = nodeSetPtr[ iSide ];
9640     TIDSortedElemSet             * elemSet = elemSetPtr[ iSide ];
9641     TIDSortedElemSet             * faceSet = faceSetPtr[ iSide ];
9642     set<const SMDS_MeshElement*> * volSet  = volSetPtr [ iSide ];
9643     set<const SMDS_MeshElement*>::iterator vIt;
9644     TIDSortedElemSet::iterator eIt;
9645     set<const SMDS_MeshNode*>::iterator    nIt;
9646
9647     // check that given nodes belong to given elements
9648     const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9649     const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9650     int firstIndex = -1, secondIndex = -1;
9651     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9652       const SMDS_MeshElement* elem = *eIt;
9653       if ( firstIndex  < 0 ) firstIndex  = elem->GetNodeIndex( n1 );
9654       if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9655       if ( firstIndex > -1 && secondIndex > -1 ) break;
9656     }
9657     if ( firstIndex < 0 || secondIndex < 0 ) {
9658       // we can simply return until temporary faces created
9659       return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9660     }
9661
9662     // -----------------------------------------------------------
9663     // 1a. Collect nodes of existing faces
9664     //     and build set of face nodes in order to detect missing
9665     //     faces corresponding to sides of volumes
9666     // -----------------------------------------------------------
9667
9668     set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9669
9670     // loop on the given element of a side
9671     for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9672       //const SMDS_MeshElement* elem = *eIt;
9673       const SMDS_MeshElement* elem = *eIt;
9674       if ( elem->GetType() == SMDSAbs_Face ) {
9675         faceSet->insert( elem );
9676         set <const SMDS_MeshNode*> faceNodeSet;
9677         SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9678         while ( nodeIt->more() ) {
9679           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9680           nodeSet->insert( n );
9681           faceNodeSet.insert( n );
9682         }
9683         setOfFaceNodeSet.insert( faceNodeSet );
9684       }
9685       else if ( elem->GetType() == SMDSAbs_Volume )
9686         volSet->insert( elem );
9687     }
9688     // ------------------------------------------------------------------------------
9689     // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9690     // ------------------------------------------------------------------------------
9691
9692     for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9693       SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9694       while ( fIt->more() ) { // loop on faces sharing a node
9695         const SMDS_MeshElement* f = fIt->next();
9696         if ( faceSet->find( f ) == faceSet->end() ) {
9697           // check if all nodes are in nodeSet and
9698           // complete setOfFaceNodeSet if they are
9699           set <const SMDS_MeshNode*> faceNodeSet;
9700           SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9701           bool allInSet = true;
9702           while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9703             const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9704             if ( nodeSet->find( n ) == nodeSet->end() )
9705               allInSet = false;
9706             else
9707               faceNodeSet.insert( n );
9708           }
9709           if ( allInSet ) {
9710             faceSet->insert( f );
9711             setOfFaceNodeSet.insert( faceNodeSet );
9712           }
9713         }
9714       }
9715     }
9716
9717     // -------------------------------------------------------------------------
9718     // 1c. Create temporary faces representing sides of volumes if correspondent
9719     //     face does not exist
9720     // -------------------------------------------------------------------------
9721
9722     if ( !volSet->empty() ) {
9723       //int nodeSetSize = nodeSet->size();
9724
9725       // loop on given volumes
9726       for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9727         SMDS_VolumeTool vol (*vIt);
9728         // loop on volume faces: find free faces
9729         // --------------------------------------
9730         list<const SMDS_MeshElement* > freeFaceList;
9731         for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9732           if ( !vol.IsFreeFace( iFace ))
9733             continue;
9734           // check if there is already a face with same nodes in a face set
9735           const SMDS_MeshElement* aFreeFace = 0;
9736           const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9737           int nbNodes = vol.NbFaceNodes( iFace );
9738           set <const SMDS_MeshNode*> faceNodeSet;
9739           vol.GetFaceNodes( iFace, faceNodeSet );
9740           bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9741           if ( isNewFace ) {
9742             // no such a face is given but it still can exist, check it
9743             vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9744             aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9745           }
9746           if ( !aFreeFace ) {
9747             // create a temporary face
9748             if ( nbNodes == 3 ) {
9749               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9750               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9751             }
9752             else if ( nbNodes == 4 ) {
9753               //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9754               aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9755             }
9756             else {
9757               vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9758               //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9759               aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9760             }
9761             if ( aFreeFace )
9762               tempFaceList.push_back( aFreeFace );
9763           }
9764
9765           if ( aFreeFace )
9766             freeFaceList.push_back( aFreeFace );
9767
9768         } // loop on faces of a volume
9769
9770         // choose one of several free faces of a volume
9771         // --------------------------------------------
9772         if ( freeFaceList.size() > 1 ) {
9773           // choose a face having max nb of nodes shared by other elems of a side
9774           int maxNbNodes = -1;
9775           list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9776           while ( fIt != freeFaceList.end() ) { // loop on free faces
9777             int nbSharedNodes = 0;
9778             SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9779             while ( nodeIt->more() ) { // loop on free face nodes
9780               const SMDS_MeshNode* n =
9781                 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9782               SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9783               while ( invElemIt->more() ) {
9784                 const SMDS_MeshElement* e = invElemIt->next();
9785                 nbSharedNodes += faceSet->count( e );
9786                 nbSharedNodes += elemSet->count( e );
9787               }
9788             }
9789             if ( nbSharedNodes > maxNbNodes ) {
9790               maxNbNodes = nbSharedNodes;
9791               freeFaceList.erase( freeFaceList.begin(), fIt++ );
9792             }
9793             else if ( nbSharedNodes == maxNbNodes ) {
9794               fIt++;
9795             }
9796             else {
9797               freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9798             }
9799           }
9800           if ( freeFaceList.size() > 1 )
9801           {
9802             // could not choose one face, use another way
9803             // choose a face most close to the bary center of the opposite side
9804             gp_XYZ aBC( 0., 0., 0. );
9805             set <const SMDS_MeshNode*> addedNodes;
9806             TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9807             eIt = elemSet2->begin();
9808             for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9809               SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9810               while ( nodeIt->more() ) { // loop on free face nodes
9811                 const SMDS_MeshNode* n =
9812                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9813                 if ( addedNodes.insert( n ).second )
9814                   aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9815               }
9816             }
9817             aBC /= addedNodes.size();
9818             double minDist = DBL_MAX;
9819             fIt = freeFaceList.begin();
9820             while ( fIt != freeFaceList.end() ) { // loop on free faces
9821               double dist = 0;
9822               SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9823               while ( nodeIt->more() ) { // loop on free face nodes
9824                 const SMDS_MeshNode* n =
9825                   static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9826                 gp_XYZ p( n->X(),n->Y(),n->Z() );
9827                 dist += ( aBC - p ).SquareModulus();
9828               }
9829               if ( dist < minDist ) {
9830                 minDist = dist;
9831                 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9832               }
9833               else
9834                 fIt = freeFaceList.erase( fIt++ );
9835             }
9836           }
9837         } // choose one of several free faces of a volume
9838
9839         if ( freeFaceList.size() == 1 ) {
9840           const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9841           faceSet->insert( aFreeFace );
9842           // complete a node set with nodes of a found free face
9843           //           for ( iNode = 0; iNode < ; iNode++ )
9844           //             nodeSet->insert( fNodes[ iNode ] );
9845         }
9846
9847       } // loop on volumes of a side
9848
9849       //       // complete a set of faces if new nodes in a nodeSet appeared
9850       //       // ----------------------------------------------------------
9851       //       if ( nodeSetSize != nodeSet->size() ) {
9852       //         for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9853       //           SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9854       //           while ( fIt->more() ) { // loop on faces sharing a node
9855       //             const SMDS_MeshElement* f = fIt->next();
9856       //             if ( faceSet->find( f ) == faceSet->end() ) {
9857       //               // check if all nodes are in nodeSet and
9858       //               // complete setOfFaceNodeSet if they are
9859       //               set <const SMDS_MeshNode*> faceNodeSet;
9860       //               SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9861       //               bool allInSet = true;
9862       //               while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9863       //                 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9864       //                 if ( nodeSet->find( n ) == nodeSet->end() )
9865       //                   allInSet = false;
9866       //                 else
9867       //                   faceNodeSet.insert( n );
9868       //               }
9869       //               if ( allInSet ) {
9870       //                 faceSet->insert( f );
9871       //                 setOfFaceNodeSet.insert( faceNodeSet );
9872       //               }
9873       //             }
9874       //           }
9875       //         }
9876       //       }
9877     } // Create temporary faces, if there are volumes given
9878   } // loop on sides
9879
9880   if ( faceSet1.size() != faceSet2.size() ) {
9881     // delete temporary faces: they are in reverseElements of actual nodes
9882     //    SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9883     //    while ( tmpFaceIt->more() )
9884     //      aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9885     //    list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9886     //    for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9887     //      aMesh->RemoveElement(*tmpFaceIt);
9888     MESSAGE("Diff nb of faces");
9889     return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9890   }
9891
9892   // ============================================================
9893   // 2. Find nodes to merge:
9894   //              bind a node to remove to a node to put instead
9895   // ============================================================
9896
9897   TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9898   if ( theFirstNode1 != theFirstNode2 )
9899     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9900   if ( theSecondNode1 != theSecondNode2 )
9901     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9902
9903   LinkID_Gen aLinkID_Gen( GetMeshDS() );
9904   set< long > linkIdSet; // links to process
9905   linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9906
9907   typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9908   list< NLink > linkList[2];
9909   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9910   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9911   // loop on links in linkList; find faces by links and append links
9912   // of the found faces to linkList
9913   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9914   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9915   {
9916     NLink link[] = { *linkIt[0], *linkIt[1] };
9917     long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9918     if ( !linkIdSet.count( linkID ) )
9919       continue;
9920
9921     // by links, find faces in the face sets,
9922     // and find indices of link nodes in the found faces;
9923     // in a face set, there is only one or no face sharing a link
9924     // ---------------------------------------------------------------
9925
9926     const SMDS_MeshElement* face[] = { 0, 0 };
9927     vector<const SMDS_MeshNode*> fnodes[2];
9928     int iLinkNode[2][2];
9929     TIDSortedElemSet avoidSet;
9930     for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9931       const SMDS_MeshNode* n1 = link[iSide].first;
9932       const SMDS_MeshNode* n2 = link[iSide].second;
9933       //cout << "Side " << iSide << " ";
9934       //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9935       // find a face by two link nodes
9936       face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9937                                                       *faceSetPtr[ iSide ], avoidSet,
9938                                                       &iLinkNode[iSide][0],
9939                                                       &iLinkNode[iSide][1] );
9940       if ( face[ iSide ])
9941       {
9942         //cout << " F " << face[ iSide]->GetID() <<endl;
9943         faceSetPtr[ iSide ]->erase( face[ iSide ]);
9944         // put face nodes to fnodes
9945         if ( face[ iSide ]->IsQuadratic() )
9946         {
9947           // use interlaced nodes iterator
9948           const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9949           if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9950           SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9951           while ( nIter->more() )
9952             fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9953         }
9954         else
9955         {
9956           fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9957                                   face[ iSide ]->end_nodes() );
9958         }
9959         fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9960       }
9961     }
9962
9963     // check similarity of elements of the sides
9964     if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9965       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9966       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9967         aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9968       }
9969       else {
9970         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9971       }
9972       break; // do not return because it's necessary to remove tmp faces
9973     }
9974
9975     // set nodes to merge
9976     // -------------------
9977
9978     if ( face[0] && face[1] )  {
9979       const int nbNodes = face[0]->NbNodes();
9980       if ( nbNodes != face[1]->NbNodes() ) {
9981         MESSAGE("Diff nb of face nodes");
9982         aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9983         break; // do not return because it s necessary to remove tmp faces
9984       }
9985       bool reverse[] = { false, false }; // order of nodes in the link
9986       for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9987         // analyse link orientation in faces
9988         int i1 = iLinkNode[ iSide ][ 0 ];
9989         int i2 = iLinkNode[ iSide ][ 1 ];
9990         reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9991       }
9992       int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9993       int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9994       for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9995       {
9996         nReplaceMap.insert  ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9997                                           fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9998       }
9999
10000       // add other links of the faces to linkList
10001       // -----------------------------------------
10002
10003       for ( iNode = 0; iNode < nbNodes; iNode++ )  {
10004         linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10005         pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10006         if ( !iter_isnew.second ) { // already in a set: no need to process
10007           linkIdSet.erase( iter_isnew.first );
10008         }
10009         else // new in set == encountered for the first time: add
10010         {
10011           const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10012           const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10013           linkList[0].push_back ( NLink( n1, n2 ));
10014           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10015         }
10016       }
10017     } // 2 faces found
10018
10019     if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10020       break;
10021
10022   } // loop on link lists
10023
10024   if ( aResult == SEW_OK &&
10025        ( //linkIt[0] != linkList[0].end() ||
10026         !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10027     MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10028              " " << (faceSetPtr[1]->empty()));
10029     aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10030   }
10031
10032   // ====================================================================
10033   // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10034   // ====================================================================
10035
10036   // delete temporary faces
10037   //  SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10038   //  while ( tmpFaceIt->more() )
10039   //    aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10040   list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10041   for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10042     aMesh->RemoveElement(*tmpFaceIt);
10043
10044   if ( aResult != SEW_OK)
10045     return aResult;
10046
10047   list< int > nodeIDsToRemove;
10048   vector< const SMDS_MeshNode*> nodes;
10049   ElemFeatures elemType;
10050
10051   // loop on nodes replacement map
10052   TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10053   for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10054     if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10055     {
10056       const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10057       nodeIDsToRemove.push_back( nToRemove->GetID() );
10058       // loop on elements sharing nToRemove
10059       SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10060       while ( invElemIt->more() ) {
10061         const SMDS_MeshElement* e = invElemIt->next();
10062         // get a new suite of nodes: make replacement
10063         int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10064         nodes.resize( nbNodes );
10065         SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10066         while ( nIt->more() ) {
10067           const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10068           nnIt = nReplaceMap.find( n );
10069           if ( nnIt != nReplaceMap.end() ) {
10070             nbReplaced++;
10071             n = (*nnIt).second;
10072           }
10073           nodes[ i++ ] = n;
10074         }
10075         //       if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10076         //         elemIDsToRemove.push_back( e->GetID() );
10077         //       else
10078         if ( nbReplaced )
10079         {
10080           elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10081           aMesh->RemoveElement( e );
10082
10083           if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10084           {
10085             AddToSameGroups( newElem, e, aMesh );
10086             if ( int aShapeId = e->getshapeId() )
10087               aMesh->SetMeshElementOnShape( newElem, aShapeId );
10088           }
10089         }
10090       }
10091     }
10092
10093   Remove( nodeIDsToRemove, true );
10094
10095   return aResult;
10096 }
10097
10098 //================================================================================
10099 /*!
10100  * \brief Find corresponding nodes in two sets of faces
10101  * \param theSide1 - first face set
10102  * \param theSide2 - second first face
10103  * \param theFirstNode1 - a boundary node of set 1
10104  * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10105  * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10106  * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10107  * \param nReplaceMap - output map of corresponding nodes
10108  * \return bool  - is a success or not
10109  */
10110 //================================================================================
10111
10112 #ifdef _DEBUG_
10113 //#define DEBUG_MATCHING_NODES
10114 #endif
10115
10116 SMESH_MeshEditor::Sew_Error
10117 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10118                                     set<const SMDS_MeshElement*>& theSide2,
10119                                     const SMDS_MeshNode*          theFirstNode1,
10120                                     const SMDS_MeshNode*          theFirstNode2,
10121                                     const SMDS_MeshNode*          theSecondNode1,
10122                                     const SMDS_MeshNode*          theSecondNode2,
10123                                     TNodeNodeMap &                nReplaceMap)
10124 {
10125   set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10126
10127   nReplaceMap.clear();
10128   if ( theFirstNode1 != theFirstNode2 )
10129     nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10130   if ( theSecondNode1 != theSecondNode2 )
10131     nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10132
10133   set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10134   linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10135
10136   list< NLink > linkList[2];
10137   linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10138   linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10139
10140   // loop on links in linkList; find faces by links and append links
10141   // of the found faces to linkList
10142   list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10143   for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10144     NLink link[] = { *linkIt[0], *linkIt[1] };
10145     if ( linkSet.find( link[0] ) == linkSet.end() )
10146       continue;
10147
10148     // by links, find faces in the face sets,
10149     // and find indices of link nodes in the found faces;
10150     // in a face set, there is only one or no face sharing a link
10151     // ---------------------------------------------------------------
10152
10153     const SMDS_MeshElement* face[] = { 0, 0 };
10154     list<const SMDS_MeshNode*> notLinkNodes[2];
10155     //bool reverse[] = { false, false }; // order of notLinkNodes
10156     int nbNodes[2];
10157     for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10158     {
10159       const SMDS_MeshNode* n1 = link[iSide].first;
10160       const SMDS_MeshNode* n2 = link[iSide].second;
10161       set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10162       set< const SMDS_MeshElement* > facesOfNode1;
10163       for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10164       {
10165         // during a loop of the first node, we find all faces around n1,
10166         // during a loop of the second node, we find one face sharing both n1 and n2
10167         const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10168         SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10169         while ( fIt->more() ) { // loop on faces sharing a node
10170           const SMDS_MeshElement* f = fIt->next();
10171           if (faceSet->find( f ) != faceSet->end() && // f is in face set
10172               ! facesOfNode1.insert( f ).second ) // f encounters twice
10173           {
10174             if ( face[ iSide ] ) {
10175               MESSAGE( "2 faces per link " );
10176               return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10177             }
10178             face[ iSide ] = f;
10179             faceSet->erase( f );
10180
10181             // get not link nodes
10182             int nbN = f->NbNodes();
10183             if ( f->IsQuadratic() )
10184               nbN /= 2;
10185             nbNodes[ iSide ] = nbN;
10186             list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10187             int i1 = f->GetNodeIndex( n1 );
10188             int i2 = f->GetNodeIndex( n2 );
10189             int iEnd = nbN, iBeg = -1, iDelta = 1;
10190             bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10191             if ( reverse ) {
10192               std::swap( iEnd, iBeg ); iDelta = -1;
10193             }
10194             int i = i2;
10195             while ( true ) {
10196               i += iDelta;
10197               if ( i == iEnd ) i = iBeg + iDelta;
10198               if ( i == i1 ) break;
10199               nodes.push_back ( f->GetNode( i ) );
10200             }
10201           }
10202         }
10203       }
10204     }
10205     // check similarity of elements of the sides
10206     if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10207       MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10208       if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10209         return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10210       }
10211       else {
10212         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10213       }
10214     }
10215
10216     // set nodes to merge
10217     // -------------------
10218
10219     if ( face[0] && face[1] )  {
10220       if ( nbNodes[0] != nbNodes[1] ) {
10221         MESSAGE("Diff nb of face nodes");
10222         return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10223       }
10224 #ifdef DEBUG_MATCHING_NODES
10225       MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10226                 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10227                 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10228 #endif
10229       int nbN = nbNodes[0];
10230       {
10231         list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10232         list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10233         for ( int i = 0 ; i < nbN - 2; ++i ) {
10234 #ifdef DEBUG_MATCHING_NODES
10235           MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10236 #endif
10237           nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10238         }
10239       }
10240
10241       // add other links of the face 1 to linkList
10242       // -----------------------------------------
10243
10244       const SMDS_MeshElement* f0 = face[0];
10245       const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10246       for ( int i = 0; i < nbN; i++ )
10247       {
10248         const SMDS_MeshNode* n2 = f0->GetNode( i );
10249         pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10250           linkSet.insert( SMESH_TLink( n1, n2 ));
10251         if ( !iter_isnew.second ) { // already in a set: no need to process
10252           linkSet.erase( iter_isnew.first );
10253         }
10254         else // new in set == encountered for the first time: add
10255         {
10256 #ifdef DEBUG_MATCHING_NODES
10257           MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10258                     << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10259 #endif
10260           linkList[0].push_back ( NLink( n1, n2 ));
10261           linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10262         }
10263         n1 = n2;
10264       }
10265     } // 2 faces found
10266   } // loop on link lists
10267
10268   return SEW_OK;
10269 }
10270
10271 //================================================================================
10272 /*!
10273  * \brief Create elements equal (on same nodes) to given ones
10274  *  \param [in] theElements - a set of elems to duplicate. If it is empty, all
10275  *              elements of the uppest dimension are duplicated.
10276  */
10277 //================================================================================
10278
10279 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10280 {
10281   ClearLastCreated();
10282   SMESHDS_Mesh* mesh = GetMeshDS();
10283
10284   // get an element type and an iterator over elements
10285
10286   SMDSAbs_ElementType type = SMDSAbs_All;
10287   SMDS_ElemIteratorPtr elemIt;
10288   vector< const SMDS_MeshElement* > allElems;
10289   if ( theElements.empty() )
10290   {
10291     if ( mesh->NbNodes() == 0 )
10292       return;
10293     // get most complex type
10294     SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10295       SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10296       SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10297     };
10298     for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10299       if ( mesh->GetMeshInfo().NbElements( types[i] ))
10300       {
10301         type = types[i];
10302         break;
10303       }
10304     elemIt = mesh->elementsIterator( type );
10305   }
10306   else
10307   {
10308     type = (*theElements.begin())->GetType();
10309     elemIt = SMESHUtils::elemSetIterator( theElements );
10310   }
10311
10312   // duplicate elements
10313
10314   ElemFeatures elemType;
10315
10316   vector< const SMDS_MeshNode* > nodes;
10317   while ( elemIt->more() )
10318   {
10319     const SMDS_MeshElement* elem = elemIt->next();
10320     if ( elem->GetType() != type )
10321       continue;
10322
10323     elemType.Init( elem, /*basicOnly=*/false );
10324     nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10325
10326     AddElement( nodes, elemType );
10327   }
10328 }
10329
10330 //================================================================================
10331 /*!
10332   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10333   \param theElems - the list of elements (edges or faces) to be replicated
10334   The nodes for duplication could be found from these elements
10335   \param theNodesNot - list of nodes to NOT replicate
10336   \param theAffectedElems - the list of elements (cells and edges) to which the
10337   replicated nodes should be associated to.
10338   \return TRUE if operation has been completed successfully, FALSE otherwise
10339 */
10340 //================================================================================
10341
10342 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10343                                     const TIDSortedElemSet& theNodesNot,
10344                                     const TIDSortedElemSet& theAffectedElems )
10345 {
10346   ClearLastCreated();
10347
10348   if ( theElems.size() == 0 )
10349     return false;
10350
10351   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10352   if ( !aMeshDS )
10353     return false;
10354
10355   bool res = false;
10356   TNodeNodeMap anOldNodeToNewNode;
10357   // duplicate elements and nodes
10358   res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10359   // replce nodes by duplications
10360   res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10361   return res;
10362 }
10363
10364 //================================================================================
10365 /*!
10366   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10367   \param theMeshDS - mesh instance
10368   \param theElems - the elements replicated or modified (nodes should be changed)
10369   \param theNodesNot - nodes to NOT replicate
10370   \param theNodeNodeMap - relation of old node to new created node
10371   \param theIsDoubleElem - flag os to replicate element or modify
10372   \return TRUE if operation has been completed successfully, FALSE otherwise
10373 */
10374 //================================================================================
10375
10376 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh*           theMeshDS,
10377                                    const TIDSortedElemSet& theElems,
10378                                    const TIDSortedElemSet& theNodesNot,
10379                                    TNodeNodeMap&           theNodeNodeMap,
10380                                    const bool              theIsDoubleElem )
10381 {
10382   // iterate through element and duplicate them (by nodes duplication)
10383   bool res = false;
10384   std::vector<const SMDS_MeshNode*> newNodes;
10385   ElemFeatures elemType;
10386
10387   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10388   for ( ;  elemItr != theElems.end(); ++elemItr )
10389   {
10390     const SMDS_MeshElement* anElem = *elemItr;
10391     if (!anElem)
10392       continue;
10393
10394     // duplicate nodes to duplicate element
10395     bool isDuplicate = false;
10396     newNodes.resize( anElem->NbNodes() );
10397     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10398     int ind = 0;
10399     while ( anIter->more() )
10400     {
10401       const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10402       const SMDS_MeshNode*  aNewNode = aCurrNode;
10403       TNodeNodeMap::iterator     n2n = theNodeNodeMap.find( aCurrNode );
10404       if ( n2n != theNodeNodeMap.end() )
10405       {
10406         aNewNode = n2n->second;
10407       }
10408       else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10409       {
10410         // duplicate node
10411         aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10412         copyPosition( aCurrNode, aNewNode );
10413         theNodeNodeMap[ aCurrNode ] = aNewNode;
10414         myLastCreatedNodes.push_back( aNewNode );
10415       }
10416       isDuplicate |= (aCurrNode != aNewNode);
10417       newNodes[ ind++ ] = aNewNode;
10418     }
10419     if ( !isDuplicate )
10420       continue;
10421
10422     if ( theIsDoubleElem )
10423       AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10424     else
10425       theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10426
10427     res = true;
10428   }
10429   return res;
10430 }
10431
10432 //================================================================================
10433 /*!
10434   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10435   \param theNodes - identifiers of nodes to be doubled
10436   \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10437   nodes. If list of element identifiers is empty then nodes are doubled but
10438   they not assigned to elements
10439   \return TRUE if operation has been completed successfully, FALSE otherwise
10440 */
10441 //================================================================================
10442
10443 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10444                                     const std::list< int >& theListOfModifiedElems )
10445 {
10446   ClearLastCreated();
10447
10448   if ( theListOfNodes.size() == 0 )
10449     return false;
10450
10451   SMESHDS_Mesh* aMeshDS = GetMeshDS();
10452   if ( !aMeshDS )
10453     return false;
10454
10455   // iterate through nodes and duplicate them
10456
10457   std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10458
10459   std::list< int >::const_iterator aNodeIter;
10460   for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10461   {
10462     int aCurr = *aNodeIter;
10463     SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10464     if ( !aNode )
10465       continue;
10466
10467     // duplicate node
10468
10469     const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10470     if ( aNewNode )
10471     {
10472       copyPosition( aNode, aNewNode );
10473       anOldNodeToNewNode[ aNode ] = aNewNode;
10474       myLastCreatedNodes.push_back( aNewNode );
10475     }
10476   }
10477
10478   // Create map of new nodes for modified elements
10479
10480   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10481
10482   std::list< int >::const_iterator anElemIter;
10483   for ( anElemIter = theListOfModifiedElems.begin();
10484         anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10485   {
10486     int aCurr = *anElemIter;
10487     SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10488     if ( !anElem )
10489       continue;
10490
10491     vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10492
10493     SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10494     int ind = 0;
10495     while ( anIter->more() )
10496     {
10497       SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10498       if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10499       {
10500         const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10501         aNodeArr[ ind++ ] = aNewNode;
10502       }
10503       else
10504         aNodeArr[ ind++ ] = aCurrNode;
10505     }
10506     anElemToNodes[ anElem ] = aNodeArr;
10507   }
10508
10509   // Change nodes of elements
10510
10511   std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10512     anElemToNodesIter = anElemToNodes.begin();
10513   for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10514   {
10515     const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10516     vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10517     if ( anElem )
10518     {
10519       aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10520     }
10521   }
10522
10523   return true;
10524 }
10525
10526 namespace {
10527
10528   //================================================================================
10529   /*!
10530     \brief Check if element located inside shape
10531     \return TRUE if IN or ON shape, FALSE otherwise
10532   */
10533   //================================================================================
10534
10535   template<class Classifier>
10536   bool isInside(const SMDS_MeshElement* theElem,
10537                 Classifier&             theClassifier,
10538                 const double            theTol)
10539   {
10540     gp_XYZ centerXYZ (0, 0, 0);
10541     SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10542     while (aNodeItr->more())
10543       centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10544
10545     gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10546     theClassifier.Perform(aPnt, theTol);
10547     TopAbs_State aState = theClassifier.State();
10548     return (aState == TopAbs_IN || aState == TopAbs_ON );
10549   }
10550
10551   //================================================================================
10552   /*!
10553    * \brief Classifier of the 3D point on the TopoDS_Face
10554    *        with interaface suitable for isInside()
10555    */
10556   //================================================================================
10557
10558   struct _FaceClassifier
10559   {
10560     Extrema_ExtPS       _extremum;
10561     BRepAdaptor_Surface _surface;
10562     TopAbs_State        _state;
10563
10564     _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10565     {
10566       _extremum.Initialize( _surface,
10567                             _surface.FirstUParameter(), _surface.LastUParameter(),
10568                             _surface.FirstVParameter(), _surface.LastVParameter(),
10569                             _surface.Tolerance(), _surface.Tolerance() );
10570     }
10571     void Perform(const gp_Pnt& aPnt, double theTol)
10572     {
10573       theTol *= theTol;
10574       _state = TopAbs_OUT;
10575       _extremum.Perform(aPnt);
10576       if ( _extremum.IsDone() )
10577         for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10578           _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10579     }
10580     TopAbs_State State() const
10581     {
10582       return _state;
10583     }
10584   };
10585 }
10586
10587 //================================================================================
10588 /*!
10589   \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10590   This method is the first step of DoubleNodeElemGroupsInRegion.
10591   \param theElems - list of groups of elements (edges or faces) to be replicated
10592   \param theNodesNot - list of groups of nodes not to replicated
10593   \param theShape - shape to detect affected elements (element which geometric center
10594          located on or inside shape). If the shape is null, detection is done on faces orientations
10595          (select elements with a gravity center on the side given by faces normals).
10596          This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10597          The replicated nodes should be associated to affected elements.
10598   \return groups of affected elements
10599   \sa DoubleNodeElemGroupsInRegion()
10600  */
10601 //================================================================================
10602
10603 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10604                                                    const TIDSortedElemSet& theNodesNot,
10605                                                    const TopoDS_Shape&     theShape,
10606                                                    TIDSortedElemSet&       theAffectedElems)
10607 {
10608   if ( theShape.IsNull() )
10609   {
10610     std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10611     std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10612     std::set<const SMDS_MeshElement*> edgesToCheck;
10613     alreadyCheckedNodes.clear();
10614     alreadyCheckedElems.clear();
10615     edgesToCheck.clear();
10616
10617     // --- iterates on elements to be replicated and get elements by back references from their nodes
10618
10619     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10620     for ( ;  elemItr != theElems.end(); ++elemItr )
10621     {
10622       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10623       if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10624         continue;
10625       gp_XYZ normal;
10626       SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10627       std::set<const SMDS_MeshNode*> nodesElem;
10628       nodesElem.clear();
10629       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10630       while ( nodeItr->more() )
10631       {
10632         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10633         nodesElem.insert(aNode);
10634       }
10635       std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10636       for (; nodit != nodesElem.end(); nodit++)
10637       {
10638         const SMDS_MeshNode* aNode = *nodit;
10639         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10640           continue;
10641         if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10642           continue;
10643         alreadyCheckedNodes.insert(aNode);
10644         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10645         while ( backElemItr->more() )
10646         {
10647           const SMDS_MeshElement* curElem = backElemItr->next();
10648           if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10649             continue;
10650           if (theElems.find(curElem) != theElems.end())
10651             continue;
10652           alreadyCheckedElems.insert(curElem);
10653           double x=0, y=0, z=0;
10654           int nb = 0;
10655           SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10656           while ( nodeItr2->more() )
10657           {
10658             const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10659             x += anotherNode->X();
10660             y += anotherNode->Y();
10661             z += anotherNode->Z();
10662             nb++;
10663           }
10664           gp_XYZ p;
10665           p.SetCoord( x/nb -aNode->X(),
10666                       y/nb -aNode->Y(),
10667                       z/nb -aNode->Z() );
10668           if (normal*p > 0)
10669           {
10670             theAffectedElems.insert( curElem );
10671           }
10672           else if (curElem->GetType() == SMDSAbs_Edge)
10673             edgesToCheck.insert(curElem);
10674         }
10675       }
10676     }
10677     // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10678     std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10679     for( ; eit != edgesToCheck.end(); eit++)
10680     {
10681       bool onside = true;
10682       const SMDS_MeshElement* anEdge = *eit;
10683       SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10684       while ( nodeItr->more() )
10685       {
10686         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10687         if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10688         {
10689           onside = false;
10690           break;
10691         }
10692       }
10693       if (onside)
10694       {
10695         theAffectedElems.insert(anEdge);
10696       }
10697     }
10698   }
10699   else
10700   {
10701     const double aTol = Precision::Confusion();
10702     auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10703     auto_ptr<_FaceClassifier>              aFaceClassifier;
10704     if ( theShape.ShapeType() == TopAbs_SOLID )
10705     {
10706       bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10707       bsc3d->PerformInfinitePoint(aTol);
10708     }
10709     else if (theShape.ShapeType() == TopAbs_FACE )
10710     {
10711       aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10712     }
10713
10714     // iterates on indicated elements and get elements by back references from their nodes
10715     TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10716     for ( ;  elemItr != theElems.end(); ++elemItr )
10717     {
10718       SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10719       if (!anElem)
10720         continue;
10721       SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10722       while ( nodeItr->more() )
10723       {
10724         const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10725         if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10726           continue;
10727         SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10728         while ( backElemItr->more() )
10729         {
10730           const SMDS_MeshElement* curElem = backElemItr->next();
10731           if ( curElem && theElems.find(curElem) == theElems.end() &&
10732               ( bsc3d.get() ?
10733                 isInside( curElem, *bsc3d, aTol ) :
10734                 isInside( curElem, *aFaceClassifier, aTol )))
10735             theAffectedElems.insert( curElem );
10736         }
10737       }
10738     }
10739   }
10740   return true;
10741 }
10742
10743 //================================================================================
10744 /*!
10745   \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10746   \param theElems - group of of elements (edges or faces) to be replicated
10747   \param theNodesNot - group of nodes not to replicate
10748   \param theShape - shape to detect affected elements (element which geometric center
10749   located on or inside shape).
10750   The replicated nodes should be associated to affected elements.
10751   \return TRUE if operation has been completed successfully, FALSE otherwise
10752 */
10753 //================================================================================
10754
10755 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10756                                             const TIDSortedElemSet& theNodesNot,
10757                                             const TopoDS_Shape&     theShape )
10758 {
10759   if ( theShape.IsNull() )
10760     return false;
10761
10762   const double aTol = Precision::Confusion();
10763   SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10764   SMESHUtils::Deleter<_FaceClassifier>              aFaceClassifier;
10765   if ( theShape.ShapeType() == TopAbs_SOLID )
10766   {
10767     bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10768     bsc3d->PerformInfinitePoint(aTol);
10769   }
10770   else if (theShape.ShapeType() == TopAbs_FACE )
10771   {
10772     aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10773   }
10774
10775   // iterates on indicated elements and get elements by back references from their nodes
10776   TIDSortedElemSet anAffected;
10777   TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10778   for ( ;  elemItr != theElems.end(); ++elemItr )
10779   {
10780     SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10781     if (!anElem)
10782       continue;
10783
10784     SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10785     while ( nodeItr->more() )
10786     {
10787       const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10788       if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10789         continue;
10790       SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10791       while ( backElemItr->more() )
10792       {
10793         const SMDS_MeshElement* curElem = backElemItr->next();
10794         if ( curElem && theElems.find(curElem) == theElems.end() &&
10795              ( bsc3d ?
10796                isInside( curElem, *bsc3d, aTol ) :
10797                isInside( curElem, *aFaceClassifier, aTol )))
10798           anAffected.insert( curElem );
10799       }
10800     }
10801   }
10802   return DoubleNodes( theElems, theNodesNot, anAffected );
10803 }
10804
10805 /*!
10806  *  \brief compute an oriented angle between two planes defined by four points.
10807  *  The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10808  *  @param p0 base of the rotation axe
10809  *  @param p1 extremity of the rotation axe
10810  *  @param g1 belongs to the first plane
10811  *  @param g2 belongs to the second plane
10812  */
10813 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10814 {
10815   gp_Vec vref(p0, p1);
10816   gp_Vec v1(p0, g1);
10817   gp_Vec v2(p0, g2);
10818   gp_Vec n1 = vref.Crossed(v1);
10819   gp_Vec n2 = vref.Crossed(v2);
10820   try {
10821     return n2.AngleWithRef(n1, vref);
10822   }
10823   catch ( Standard_Failure ) {
10824   }
10825   return Max( v1.Magnitude(), v2.Magnitude() );
10826 }
10827
10828 /*!
10829  * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10830  *  The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10831  * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10832  * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10833  * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10834  * 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.
10835  * 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.
10836  * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10837  * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10838  * \param theElems - list of groups of volumes, where a group of volume is a set of
10839  *        SMDS_MeshElements sorted by Id.
10840  * \param createJointElems - if TRUE, create the elements
10841  * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10842  *        the boundary between \a theDomains and the rest mesh
10843  * \return TRUE if operation has been completed successfully, FALSE otherwise
10844  */
10845 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10846                                                      bool                                 createJointElems,
10847                                                      bool                                 onAllBoundaries)
10848 {
10849   MESSAGE("----------------------------------------------");
10850   MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10851   MESSAGE("----------------------------------------------");
10852
10853   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10854   meshDS->BuildDownWardConnectivity(true);
10855   CHRONO(50);
10856   SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10857
10858   // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10859   //     build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10860   //     build the list of nodes shared by 2 or more domains, with their domain indexes
10861
10862   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10863   std::map<int,int>celldom; // cell vtkId --> domain
10864   std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains;  // oldNode --> (id domain --> id cell)
10865   std::map<int, std::map<int,int> > nodeDomains; // oldId -->  (domainId --> newId)
10866   faceDomains.clear();
10867   celldom.clear();
10868   cellDomains.clear();
10869   nodeDomains.clear();
10870   std::map<int,int> emptyMap;
10871   std::set<int> emptySet;
10872   emptyMap.clear();
10873
10874   MESSAGE(".. Number of domains :"<<theElems.size());
10875
10876   TIDSortedElemSet theRestDomElems;
10877   const int iRestDom  = -1;
10878   const int idom0     = onAllBoundaries ? iRestDom : 0;
10879   const int nbDomains = theElems.size();
10880
10881   // Check if the domains do not share an element
10882   for (int idom = 0; idom < nbDomains-1; idom++)
10883   {
10884     //       MESSAGE("... Check of domain #" << idom);
10885     const TIDSortedElemSet& domain = theElems[idom];
10886     TIDSortedElemSet::const_iterator elemItr = domain.begin();
10887     for (; elemItr != domain.end(); ++elemItr)
10888     {
10889       const SMDS_MeshElement* anElem = *elemItr;
10890       int idombisdeb = idom + 1 ;
10891       // check if the element belongs to a domain further in the list
10892       for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
10893       {
10894         const TIDSortedElemSet& domainbis = theElems[idombis];
10895         if ( domainbis.count( anElem ))
10896         {
10897           MESSAGE(".... Domain #" << idom);
10898           MESSAGE(".... Domain #" << idombis);
10899           throw SALOME_Exception("The domains are not disjoint.");
10900           return false ;
10901         }
10902       }
10903     }
10904   }
10905
10906   for (int idom = 0; idom < nbDomains; idom++)
10907   {
10908
10909     // --- build a map (face to duplicate --> volume to modify)
10910     //     with all the faces shared by 2 domains (group of elements)
10911     //     and corresponding volume of this domain, for each shared face.
10912     //     a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10913
10914     MESSAGE("... Neighbors of domain #" << idom);
10915     const TIDSortedElemSet& domain = theElems[idom];
10916     TIDSortedElemSet::const_iterator elemItr = domain.begin();
10917     for (; elemItr != domain.end(); ++elemItr)
10918     {
10919       const SMDS_MeshElement* anElem = *elemItr;
10920       if (!anElem)
10921         continue;
10922       int vtkId = anElem->getVtkId();
10923       //MESSAGE("  vtkId " << vtkId << " smdsId " << anElem->GetID());
10924       int neighborsVtkIds[NBMAXNEIGHBORS];
10925       int downIds[NBMAXNEIGHBORS];
10926       unsigned char downTypes[NBMAXNEIGHBORS];
10927       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10928       for (int n = 0; n < nbNeighbors; n++)
10929       {
10930         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10931         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10932         if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10933         {
10934           bool ok = false;
10935           for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10936           {
10937             // MESSAGE("Domain " << idombis);
10938             const TIDSortedElemSet& domainbis = theElems[idombis];
10939             if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10940           }
10941           if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10942           {
10943             DownIdType face(downIds[n], downTypes[n]);
10944             if (!faceDomains[face].count(idom))
10945             {
10946               faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10947               celldom[vtkId] = idom;
10948               //MESSAGE("       cell with a border " << vtkId << " domain " << idom);
10949             }
10950             if ( !ok )
10951             {
10952               theRestDomElems.insert( elem );
10953               faceDomains[face][iRestDom] = neighborsVtkIds[n];
10954               celldom[neighborsVtkIds[n]] = iRestDom;
10955             }
10956           }
10957         }
10958       }
10959     }
10960   }
10961
10962   //MESSAGE("Number of shared faces " << faceDomains.size());
10963   std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10964
10965   // --- explore the shared faces domain by domain,
10966   //     explore the nodes of the face and see if they belong to a cell in the domain,
10967   //     which has only a node or an edge on the border (not a shared face)
10968
10969   for (int idomain = idom0; idomain < nbDomains; idomain++)
10970   {
10971     //MESSAGE("Domain " << idomain);
10972     const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10973     itface = faceDomains.begin();
10974     for (; itface != faceDomains.end(); ++itface)
10975     {
10976       const std::map<int, int>& domvol = itface->second;
10977       if (!domvol.count(idomain))
10978         continue;
10979       DownIdType face = itface->first;
10980       //MESSAGE(" --- face " << face.cellId);
10981       std::set<int> oldNodes;
10982       oldNodes.clear();
10983       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10984       std::set<int>::iterator itn = oldNodes.begin();
10985       for (; itn != oldNodes.end(); ++itn)
10986       {
10987         int oldId = *itn;
10988         //MESSAGE("     node " << oldId);
10989         vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10990         for (int i=0; i<l.ncells; i++)
10991         {
10992           int vtkId = l.cells[i];
10993           const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10994           if (!domain.count(anElem))
10995             continue;
10996           int vtkType = grid->GetCellType(vtkId);
10997           int downId = grid->CellIdToDownId(vtkId);
10998           if (downId < 0)
10999           {
11000             MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11001             continue; // not OK at this stage of the algorithm:
11002             //no cells created after BuildDownWardConnectivity
11003           }
11004           DownIdType aCell(downId, vtkType);
11005           cellDomains[aCell][idomain] = vtkId;
11006           celldom[vtkId] = idomain;
11007           //MESSAGE("       cell " << vtkId << " domain " << idomain);
11008         }
11009       }
11010     }
11011   }
11012
11013   // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11014   //     for each shared face, get the nodes
11015   //     for each node, for each domain of the face, create a clone of the node
11016
11017   // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11018   //     junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11019   //     the value is the ordered domain ids. (more than 4 domains not taken into account)
11020
11021   std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11022   std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11023   std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11024
11025   MESSAGE(".. Duplication of the nodes");
11026   for (int idomain = idom0; idomain < nbDomains; idomain++)
11027   {
11028     itface = faceDomains.begin();
11029     for (; itface != faceDomains.end(); ++itface)
11030     {
11031       const std::map<int, int>& domvol = itface->second;
11032       if (!domvol.count(idomain))
11033         continue;
11034       DownIdType face = itface->first;
11035       //MESSAGE(" --- face " << face.cellId);
11036       std::set<int> oldNodes;
11037       oldNodes.clear();
11038       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11039       std::set<int>::iterator itn = oldNodes.begin();
11040       for (; itn != oldNodes.end(); ++itn)
11041       {
11042         int oldId = *itn;
11043         if (nodeDomains[oldId].empty())
11044         {
11045           nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11046           //MESSAGE("-+-+-b     oldNode " << oldId << " domain " << idomain);
11047         }
11048         std::map<int, int>::const_iterator itdom = domvol.begin();
11049         for (; itdom != domvol.end(); ++itdom)
11050         {
11051           int idom = itdom->first;
11052           //MESSAGE("         domain " << idom);
11053           if (!nodeDomains[oldId].count(idom)) // --- node to clone
11054           {
11055             if (nodeDomains[oldId].size() >= 2) // a multiple node
11056             {
11057               vector<int> orderedDoms;
11058               //MESSAGE("multiple node " << oldId);
11059               if (mutipleNodes.count(oldId))
11060                 orderedDoms = mutipleNodes[oldId];
11061               else
11062               {
11063                 map<int,int>::iterator it = nodeDomains[oldId].begin();
11064                 for (; it != nodeDomains[oldId].end(); ++it)
11065                   orderedDoms.push_back(it->first);
11066               }
11067               orderedDoms.push_back(idom); // TODO order ==> push_front or back
11068               //stringstream txt;
11069               //for (int i=0; i<orderedDoms.size(); i++)
11070               //  txt << orderedDoms[i] << " ";
11071               //MESSAGE("orderedDoms " << txt.str());
11072               mutipleNodes[oldId] = orderedDoms;
11073             }
11074             double *coords = grid->GetPoint(oldId);
11075             SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11076             copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11077             int newId = newNode->getVtkId();
11078             nodeDomains[oldId][idom] = newId; // cloned node for other domains
11079             //MESSAGE("-+-+-c     oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11080           }
11081         }
11082       }
11083     }
11084   }
11085
11086   MESSAGE(".. Creation of elements");
11087   for (int idomain = idom0; idomain < nbDomains; idomain++)
11088   {
11089     itface = faceDomains.begin();
11090     for (; itface != faceDomains.end(); ++itface)
11091     {
11092       std::map<int, int> domvol = itface->second;
11093       if (!domvol.count(idomain))
11094         continue;
11095       DownIdType face = itface->first;
11096       //MESSAGE(" --- face " << face.cellId);
11097       std::set<int> oldNodes;
11098       oldNodes.clear();
11099       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11100       int nbMultipleNodes = 0;
11101       std::set<int>::iterator itn = oldNodes.begin();
11102       for (; itn != oldNodes.end(); ++itn)
11103       {
11104         int oldId = *itn;
11105         if (mutipleNodes.count(oldId))
11106           nbMultipleNodes++;
11107       }
11108       if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11109       {
11110         //MESSAGE("multiple Nodes detected on a shared face");
11111         int downId = itface->first.cellId;
11112         unsigned char cellType = itface->first.cellType;
11113         // --- shared edge or shared face ?
11114         if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11115         {
11116           int nodes[3];
11117           int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11118           for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11119             if (mutipleNodes.count(nodes[i]))
11120               if (!mutipleNodesToFace.count(nodes[i]))
11121                 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11122         }
11123         else // shared face (between two volumes)
11124         {
11125           int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11126           const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11127           const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11128           for (int ie =0; ie < nbEdges; ie++)
11129           {
11130             int nodes[3];
11131             int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11132             if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11133             {
11134               vector<int> vn0 = mutipleNodes[nodes[0]];
11135               vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11136               vector<int> doms;
11137               for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11138                 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11139                   if ( vn0[i0] == vn1[i1] )
11140                     doms.push_back( vn0[ i0 ]);
11141               if ( doms.size() > 2 )
11142               {
11143                 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11144                 double *coords = grid->GetPoint(nodes[0]);
11145                 gp_Pnt p0(coords[0], coords[1], coords[2]);
11146                 coords = grid->GetPoint(nodes[nbNodes - 1]);
11147                 gp_Pnt p1(coords[0], coords[1], coords[2]);
11148                 gp_Pnt gref;
11149                 int vtkVolIds[1000];  // an edge can belong to a lot of volumes
11150                 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11151                 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11152                 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11153                 for ( size_t id = 0; id < doms.size(); id++ )
11154                 {
11155                   int idom = doms[id];
11156                   const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11157                   for ( int ivol = 0; ivol < nbvol; ivol++ )
11158                   {
11159                     int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11160                     SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11161                     if (domain.count(elem))
11162                     {
11163                       SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11164                       domvol[idom] = svol;
11165                       //MESSAGE("  domain " << idom << " volume " << elem->GetID());
11166                       double values[3];
11167                       vtkIdType npts = 0;
11168                       vtkIdType* pts = 0;
11169                       grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11170                       SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11171                       if (id ==0)
11172                       {
11173                         gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11174                         angleDom[idom] = 0;
11175                       }
11176                       else
11177                       {
11178                         gp_Pnt g(values[0], values[1], values[2]);
11179                         angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11180                         //MESSAGE("  angle=" << angleDom[idom]);
11181                       }
11182                       break;
11183                     }
11184                   }
11185                 }
11186                 map<double, int> sortedDom; // sort domains by angle
11187                 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11188                   sortedDom[ia->second] = ia->first;
11189                 vector<int> vnodes;
11190                 vector<int> vdom;
11191                 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11192                 {
11193                   vdom.push_back(ib->second);
11194                   //MESSAGE("  ordered domain " << ib->second << "  angle " << ib->first);
11195                 }
11196                 for (int ino = 0; ino < nbNodes; ino++)
11197                   vnodes.push_back(nodes[ino]);
11198                 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11199               }
11200             }
11201           }
11202         }
11203       }
11204     }
11205   }
11206
11207   // --- iterate on shared faces (volumes to modify, face to extrude)
11208   //     get node id's of the face (id SMDS = id VTK)
11209   //     create flat element with old and new nodes if requested
11210
11211   // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11212   //     (domain1 X domain2) = domain1 + MAXINT*domain2
11213
11214   std::map<int, std::map<long,int> > nodeQuadDomains;
11215   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11216
11217   MESSAGE(".. Creation of elements: simple junction");
11218   if (createJointElems)
11219   {
11220     int idg;
11221     string joints2DName = "joints2D";
11222     mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11223     SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11224     string joints3DName = "joints3D";
11225     mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11226     SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11227
11228     itface = faceDomains.begin();
11229     for (; itface != faceDomains.end(); ++itface)
11230     {
11231       DownIdType face = itface->first;
11232       std::set<int> oldNodes;
11233       std::set<int>::iterator itn;
11234       oldNodes.clear();
11235       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11236
11237       std::map<int, int> domvol = itface->second;
11238       std::map<int, int>::iterator itdom = domvol.begin();
11239       int dom1 = itdom->first;
11240       int vtkVolId = itdom->second;
11241       itdom++;
11242       int dom2 = itdom->first;
11243       SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11244                                                        nodeQuadDomains);
11245       stringstream grpname;
11246       grpname << "j_";
11247       if (dom1 < dom2)
11248         grpname << dom1 << "_" << dom2;
11249       else
11250         grpname << dom2 << "_" << dom1;
11251       string namegrp = grpname.str();
11252       if (!mapOfJunctionGroups.count(namegrp))
11253         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11254       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11255       if (sgrp)
11256         sgrp->Add(vol->GetID());
11257       if (vol->GetType() == SMDSAbs_Volume)
11258         joints3DGrp->Add(vol->GetID());
11259       else if (vol->GetType() == SMDSAbs_Face)
11260         joints2DGrp->Add(vol->GetID());
11261     }
11262   }
11263
11264   // --- create volumes on multiple domain intersection if requested
11265   //     iterate on mutipleNodesToFace
11266   //     iterate on edgesMultiDomains
11267
11268   MESSAGE(".. Creation of elements: multiple junction");
11269   if (createJointElems)
11270   {
11271     // --- iterate on mutipleNodesToFace
11272
11273     std::map<int, std::vector<int> >::iterator itn =  mutipleNodesToFace.begin();
11274     for (; itn != mutipleNodesToFace.end(); ++itn)
11275     {
11276       int node = itn->first;
11277       vector<int> orderDom = itn->second;
11278       vector<vtkIdType> orderedNodes;
11279       for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11280         orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11281       SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11282
11283       stringstream grpname;
11284       grpname << "m2j_";
11285       grpname << 0 << "_" << 0;
11286       int idg;
11287       string namegrp = grpname.str();
11288       if (!mapOfJunctionGroups.count(namegrp))
11289         mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11290       SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11291       if (sgrp)
11292         sgrp->Add(face->GetID());
11293     }
11294
11295     // --- iterate on edgesMultiDomains
11296
11297     std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11298     for (; ite != edgesMultiDomains.end(); ++ite)
11299     {
11300       vector<int> nodes = ite->first;
11301       vector<int> orderDom = ite->second;
11302       vector<vtkIdType> orderedNodes;
11303       if (nodes.size() == 2)
11304       {
11305         //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11306         for ( size_t ino = 0; ino < nodes.size(); ino++ )
11307           if ( orderDom.size() == 3 )
11308             for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11309               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11310           else
11311             for (int idom = orderDom.size()-1; idom >=0; idom--)
11312               orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11313         SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11314
11315         int idg;
11316         string namegrp = "jointsMultiples";
11317         if (!mapOfJunctionGroups.count(namegrp))
11318           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11319         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11320         if (sgrp)
11321           sgrp->Add(vol->GetID());
11322       }
11323       else
11324       {
11325         //INFOS("Quadratic multiple joints not implemented");
11326         // TODO quadratic nodes
11327       }
11328     }
11329   }
11330
11331   // --- list the explicit faces and edges of the mesh that need to be modified,
11332   //     i.e. faces and edges built with one or more duplicated nodes.
11333   //     associate these faces or edges to their corresponding domain.
11334   //     only the first domain found is kept when a face or edge is shared
11335
11336   std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11337   std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11338   faceOrEdgeDom.clear();
11339   feDom.clear();
11340
11341   MESSAGE(".. Modification of elements");
11342   for (int idomain = idom0; idomain < nbDomains; idomain++)
11343   {
11344     std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11345     for (; itnod != nodeDomains.end(); ++itnod)
11346     {
11347       int oldId = itnod->first;
11348       //MESSAGE("     node " << oldId);
11349       vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11350       for (int i = 0; i < l.ncells; i++)
11351       {
11352         int vtkId = l.cells[i];
11353         int vtkType = grid->GetCellType(vtkId);
11354         int downId = grid->CellIdToDownId(vtkId);
11355         if (downId < 0)
11356           continue; // new cells: not to be modified
11357         DownIdType aCell(downId, vtkType);
11358         int volParents[1000];
11359         int nbvol = grid->GetParentVolumes(volParents, vtkId);
11360         for (int j = 0; j < nbvol; j++)
11361           if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11362             if (!feDom.count(vtkId))
11363             {
11364               feDom[vtkId] = idomain;
11365               faceOrEdgeDom[aCell] = emptyMap;
11366               faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11367               //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11368               //        << " type " << vtkType << " downId " << downId);
11369             }
11370       }
11371     }
11372   }
11373
11374   // --- iterate on shared faces (volumes to modify, face to extrude)
11375   //     get node id's of the face
11376   //     replace old nodes by new nodes in volumes, and update inverse connectivity
11377
11378   std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11379   for (int m=0; m<3; m++)
11380   {
11381     std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11382     itface = (*amap).begin();
11383     for (; itface != (*amap).end(); ++itface)
11384     {
11385       DownIdType face = itface->first;
11386       std::set<int> oldNodes;
11387       std::set<int>::iterator itn;
11388       oldNodes.clear();
11389       grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11390       //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11391       std::map<int, int> localClonedNodeIds;
11392
11393       std::map<int, int> domvol = itface->second;
11394       std::map<int, int>::iterator itdom = domvol.begin();
11395       for (; itdom != domvol.end(); ++itdom)
11396       {
11397         int idom = itdom->first;
11398         int vtkVolId = itdom->second;
11399         //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11400         localClonedNodeIds.clear();
11401         for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11402         {
11403           int oldId = *itn;
11404           if (nodeDomains[oldId].count(idom))
11405           {
11406             localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11407             //MESSAGE("     node " << oldId << " --> " << localClonedNodeIds[oldId]);
11408           }
11409         }
11410         meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11411       }
11412     }
11413   }
11414
11415   // Remove empty groups (issue 0022812)
11416   std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11417   for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11418   {
11419     if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11420       myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11421   }
11422
11423   meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11424   grid->DeleteLinks();
11425
11426   CHRONOSTOP(50);
11427   counters::stats();
11428   return true;
11429 }
11430
11431 /*!
11432  * \brief Double nodes on some external faces and create flat elements.
11433  * Flat elements are mainly used by some types of mechanic calculations.
11434  *
11435  * Each group of the list must be constituted of faces.
11436  * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11437  * @param theElems - list of groups of faces, where a group of faces is a set of
11438  * SMDS_MeshElements sorted by Id.
11439  * @return TRUE if operation has been completed successfully, FALSE otherwise
11440  */
11441 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11442 {
11443   MESSAGE("-------------------------------------------------");
11444   MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11445   MESSAGE("-------------------------------------------------");
11446
11447   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11448
11449   // --- For each group of faces
11450   //     duplicate the nodes, create a flat element based on the face
11451   //     replace the nodes of the faces by their clones
11452
11453   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11454   std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11455   clonedNodes.clear();
11456   intermediateNodes.clear();
11457   std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11458   mapOfJunctionGroups.clear();
11459
11460   for ( size_t idom = 0; idom < theElems.size(); idom++ )
11461   {
11462     const TIDSortedElemSet&           domain = theElems[idom];
11463     TIDSortedElemSet::const_iterator elemItr = domain.begin();
11464     for ( ; elemItr != domain.end(); ++elemItr )
11465     {
11466       SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11467       SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11468       if (!aFace)
11469         continue;
11470       // MESSAGE("aFace=" << aFace->GetID());
11471       bool isQuad = aFace->IsQuadratic();
11472       vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11473
11474       // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11475
11476       SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11477       while (nodeIt->more())
11478       {
11479         const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11480         bool isMedium = isQuad && (aFace->IsMediumNode(node));
11481         if (isMedium)
11482           ln2.push_back(node);
11483         else
11484           ln0.push_back(node);
11485
11486         const SMDS_MeshNode* clone = 0;
11487         if (!clonedNodes.count(node))
11488         {
11489           clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11490           copyPosition( node, clone );
11491           clonedNodes[node] = clone;
11492         }
11493         else
11494           clone = clonedNodes[node];
11495
11496         if (isMedium)
11497           ln3.push_back(clone);
11498         else
11499           ln1.push_back(clone);
11500
11501         const SMDS_MeshNode* inter = 0;
11502         if (isQuad && (!isMedium))
11503         {
11504           if (!intermediateNodes.count(node))
11505           {
11506             inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11507             copyPosition( node, inter );
11508             intermediateNodes[node] = inter;
11509           }
11510           else
11511             inter = intermediateNodes[node];
11512           ln4.push_back(inter);
11513         }
11514       }
11515
11516       // --- extrude the face
11517
11518       vector<const SMDS_MeshNode*> ln;
11519       SMDS_MeshVolume* vol = 0;
11520       vtkIdType aType = aFace->GetVtkType();
11521       switch (aType)
11522       {
11523       case VTK_TRIANGLE:
11524         vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11525         // MESSAGE("vol prism " << vol->GetID());
11526         ln.push_back(ln1[0]);
11527         ln.push_back(ln1[1]);
11528         ln.push_back(ln1[2]);
11529         break;
11530       case VTK_QUAD:
11531         vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11532         // MESSAGE("vol hexa " << vol->GetID());
11533         ln.push_back(ln1[0]);
11534         ln.push_back(ln1[1]);
11535         ln.push_back(ln1[2]);
11536         ln.push_back(ln1[3]);
11537         break;
11538       case VTK_QUADRATIC_TRIANGLE:
11539         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11540                                 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11541         // MESSAGE("vol quad prism " << vol->GetID());
11542         ln.push_back(ln1[0]);
11543         ln.push_back(ln1[1]);
11544         ln.push_back(ln1[2]);
11545         ln.push_back(ln3[0]);
11546         ln.push_back(ln3[1]);
11547         ln.push_back(ln3[2]);
11548         break;
11549       case VTK_QUADRATIC_QUAD:
11550         //              vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11551         //                                      ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11552         //                                      ln4[0], ln4[1], ln4[2], ln4[3]);
11553         vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11554                                 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11555                                 ln4[0], ln4[1], ln4[2], ln4[3]);
11556         // MESSAGE("vol quad hexa " << vol->GetID());
11557         ln.push_back(ln1[0]);
11558         ln.push_back(ln1[1]);
11559         ln.push_back(ln1[2]);
11560         ln.push_back(ln1[3]);
11561         ln.push_back(ln3[0]);
11562         ln.push_back(ln3[1]);
11563         ln.push_back(ln3[2]);
11564         ln.push_back(ln3[3]);
11565         break;
11566       case VTK_POLYGON:
11567         break;
11568       default:
11569         break;
11570       }
11571
11572       if (vol)
11573       {
11574         stringstream grpname;
11575         grpname << "jf_";
11576         grpname << idom;
11577         int idg;
11578         string namegrp = grpname.str();
11579         if (!mapOfJunctionGroups.count(namegrp))
11580           mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11581         SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11582         if (sgrp)
11583           sgrp->Add(vol->GetID());
11584       }
11585
11586       // --- modify the face
11587
11588       aFace->ChangeNodes(&ln[0], ln.size());
11589     }
11590   }
11591   return true;
11592 }
11593
11594 /*!
11595  *  \brief identify all the elements around a geom shape, get the faces delimiting the hole
11596  *  Build groups of volume to remove, groups of faces to replace on the skin of the object,
11597  *  groups of faces to remove inside the object, (idem edges).
11598  *  Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11599  */
11600 void SMESH_MeshEditor::CreateHoleSkin(double                          radius,
11601                                       const TopoDS_Shape&             theShape,
11602                                       SMESH_NodeSearcher*             theNodeSearcher,
11603                                       const char*                     groupName,
11604                                       std::vector<double>&            nodesCoords,
11605                                       std::vector<std::vector<int> >& listOfListOfNodes)
11606 {
11607   MESSAGE("--------------------------------");
11608   MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11609   MESSAGE("--------------------------------");
11610
11611   // --- zone of volumes to remove is given :
11612   //     1 either by a geom shape (one or more vertices) and a radius,
11613   //     2 either by a group of nodes (representative of the shape)to use with the radius,
11614   //     3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11615   //     In the case 2, the group of nodes is an external group of nodes from another mesh,
11616   //     In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11617   //     defined by it's name.
11618
11619   SMESHDS_GroupBase* groupDS = 0;
11620   SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11621   while ( groupIt->more() )
11622   {
11623     groupDS = 0;
11624     SMESH_Group * group = groupIt->next();
11625     if ( !group ) continue;
11626     groupDS = group->GetGroupDS();
11627     if ( !groupDS || groupDS->IsEmpty() ) continue;
11628     std::string grpName = group->GetName();
11629     //MESSAGE("grpName=" << grpName);
11630     if (grpName == groupName)
11631       break;
11632     else
11633       groupDS = 0;
11634   }
11635
11636   bool isNodeGroup = false;
11637   bool isNodeCoords = false;
11638   if (groupDS)
11639   {
11640     if (groupDS->GetType() != SMDSAbs_Node)
11641       return;
11642     isNodeGroup = true;     // a group of nodes exists and it is in this mesh
11643   }
11644
11645   if (nodesCoords.size() > 0)
11646     isNodeCoords = true; // a list o nodes given by their coordinates
11647   //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11648
11649   // --- define groups to build
11650
11651   int idg; // --- group of SMDS volumes
11652   string grpvName = groupName;
11653   grpvName += "_vol";
11654   SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11655   if (!grp)
11656   {
11657     MESSAGE("group not created " << grpvName);
11658     return;
11659   }
11660   SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11661
11662   int idgs; // --- group of SMDS faces on the skin
11663   string grpsName = groupName;
11664   grpsName += "_skin";
11665   SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11666   if (!grps)
11667   {
11668     MESSAGE("group not created " << grpsName);
11669     return;
11670   }
11671   SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11672
11673   int idgi; // --- group of SMDS faces internal (several shapes)
11674   string grpiName = groupName;
11675   grpiName += "_internalFaces";
11676   SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11677   if (!grpi)
11678   {
11679     MESSAGE("group not created " << grpiName);
11680     return;
11681   }
11682   SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11683
11684   int idgei; // --- group of SMDS faces internal (several shapes)
11685   string grpeiName = groupName;
11686   grpeiName += "_internalEdges";
11687   SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11688   if (!grpei)
11689   {
11690     MESSAGE("group not created " << grpeiName);
11691     return;
11692   }
11693   SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11694
11695   // --- build downward connectivity
11696
11697   SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11698   meshDS->BuildDownWardConnectivity(true);
11699   SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11700
11701   // --- set of volumes detected inside
11702
11703   std::set<int> setOfInsideVol;
11704   std::set<int> setOfVolToCheck;
11705
11706   std::vector<gp_Pnt> gpnts;
11707   gpnts.clear();
11708
11709   if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11710   {
11711     MESSAGE("group of nodes provided");
11712     SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11713     while ( elemIt->more() )
11714     {
11715       const SMDS_MeshElement* elem = elemIt->next();
11716       if (!elem)
11717         continue;
11718       const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11719       if (!node)
11720         continue;
11721       SMDS_MeshElement* vol = 0;
11722       SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11723       while (volItr->more())
11724       {
11725         vol = (SMDS_MeshElement*)volItr->next();
11726         setOfInsideVol.insert(vol->getVtkId());
11727         sgrp->Add(vol->GetID());
11728       }
11729     }
11730   }
11731   else if (isNodeCoords)
11732   {
11733     MESSAGE("list of nodes coordinates provided");
11734     size_t i = 0;
11735     int k = 0;
11736     while ( i < nodesCoords.size()-2 )
11737     {
11738       double x = nodesCoords[i++];
11739       double y = nodesCoords[i++];
11740       double z = nodesCoords[i++];
11741       gp_Pnt p = gp_Pnt(x, y ,z);
11742       gpnts.push_back(p);
11743       MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11744       k++;
11745     }
11746   }
11747   else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11748   {
11749     MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11750     TopTools_IndexedMapOfShape vertexMap;
11751     TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11752     gp_Pnt p = gp_Pnt(0,0,0);
11753     if (vertexMap.Extent() < 1)
11754       return;
11755
11756     for ( int i = 1; i <= vertexMap.Extent(); ++i )
11757     {
11758       const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11759       p = BRep_Tool::Pnt(vertex);
11760       gpnts.push_back(p);
11761       MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11762     }
11763   }
11764
11765   if (gpnts.size() > 0)
11766   {
11767     int nodeId = 0;
11768     const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11769     if (startNode)
11770       nodeId = startNode->GetID();
11771     MESSAGE("nodeId " << nodeId);
11772
11773     double radius2 = radius*radius;
11774     MESSAGE("radius2 " << radius2);
11775
11776     // --- volumes on start node
11777
11778     setOfVolToCheck.clear();
11779     SMDS_MeshElement* startVol = 0;
11780     SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11781     while (volItr->more())
11782     {
11783       startVol = (SMDS_MeshElement*)volItr->next();
11784       setOfVolToCheck.insert(startVol->getVtkId());
11785     }
11786     if (setOfVolToCheck.empty())
11787     {
11788       MESSAGE("No volumes found");
11789       return;
11790     }
11791
11792     // --- starting with central volumes then their neighbors, check if they are inside
11793     //     or outside the domain, until no more new neighbor volume is inside.
11794     //     Fill the group of inside volumes
11795
11796     std::map<int, double> mapOfNodeDistance2;
11797     mapOfNodeDistance2.clear();
11798     std::set<int> setOfOutsideVol;
11799     while (!setOfVolToCheck.empty())
11800     {
11801       std::set<int>::iterator it = setOfVolToCheck.begin();
11802       int vtkId = *it;
11803       MESSAGE("volume to check,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11804       bool volInside = false;
11805       vtkIdType npts = 0;
11806       vtkIdType* pts = 0;
11807       grid->GetCellPoints(vtkId, npts, pts);
11808       for (int i=0; i<npts; i++)
11809       {
11810         double distance2 = 0;
11811         if (mapOfNodeDistance2.count(pts[i]))
11812         {
11813           distance2 = mapOfNodeDistance2[pts[i]];
11814           MESSAGE("point " << pts[i] << " distance2 " << distance2);
11815         }
11816         else
11817         {
11818           double *coords = grid->GetPoint(pts[i]);
11819           gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11820           distance2 = 1.E40;
11821           for ( size_t j = 0; j < gpnts.size(); j++ )
11822           {
11823             double d2 = aPoint.SquareDistance( gpnts[ j ]);
11824             if (d2 < distance2)
11825             {
11826               distance2 = d2;
11827               if (distance2 < radius2)
11828                 break;
11829             }
11830           }
11831           mapOfNodeDistance2[pts[i]] = distance2;
11832           MESSAGE("  point "  << pts[i]  << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " <<  coords[2]);
11833         }
11834         if (distance2 < radius2)
11835         {
11836           volInside = true; // one or more nodes inside the domain
11837           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11838           break;
11839         }
11840       }
11841       if (volInside)
11842       {
11843         setOfInsideVol.insert(vtkId);
11844         MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11845         int neighborsVtkIds[NBMAXNEIGHBORS];
11846         int downIds[NBMAXNEIGHBORS];
11847         unsigned char downTypes[NBMAXNEIGHBORS];
11848         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11849         for (int n = 0; n < nbNeighbors; n++)
11850           if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11851             setOfVolToCheck.insert(neighborsVtkIds[n]);
11852       }
11853       else
11854       {
11855         setOfOutsideVol.insert(vtkId);
11856         MESSAGE("  volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11857       }
11858       setOfVolToCheck.erase(vtkId);
11859     }
11860   }
11861
11862   // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11863   //     If yes, add the volume to the inside set
11864
11865   bool addedInside = true;
11866   std::set<int> setOfVolToReCheck;
11867   while (addedInside)
11868   {
11869     MESSAGE(" --------------------------- re check");
11870     addedInside = false;
11871     std::set<int>::iterator itv = setOfInsideVol.begin();
11872     for (; itv != setOfInsideVol.end(); ++itv)
11873     {
11874       int vtkId = *itv;
11875       int neighborsVtkIds[NBMAXNEIGHBORS];
11876       int downIds[NBMAXNEIGHBORS];
11877       unsigned char downTypes[NBMAXNEIGHBORS];
11878       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11879       for (int n = 0; n < nbNeighbors; n++)
11880         if (!setOfInsideVol.count(neighborsVtkIds[n]))
11881           setOfVolToReCheck.insert(neighborsVtkIds[n]);
11882     }
11883     setOfVolToCheck = setOfVolToReCheck;
11884     setOfVolToReCheck.clear();
11885     while  (!setOfVolToCheck.empty())
11886     {
11887       std::set<int>::iterator it = setOfVolToCheck.begin();
11888       int vtkId = *it;
11889       if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11890       {
11891         MESSAGE("volume to recheck,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11892         int countInside = 0;
11893         int neighborsVtkIds[NBMAXNEIGHBORS];
11894         int downIds[NBMAXNEIGHBORS];
11895         unsigned char downTypes[NBMAXNEIGHBORS];
11896         int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11897         for (int n = 0; n < nbNeighbors; n++)
11898           if (setOfInsideVol.count(neighborsVtkIds[n]))
11899             countInside++;
11900         MESSAGE("countInside " << countInside);
11901         if (countInside > 1)
11902         {
11903           MESSAGE("  volume inside,  vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11904           setOfInsideVol.insert(vtkId);
11905           sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11906           addedInside = true;
11907         }
11908         else
11909           setOfVolToReCheck.insert(vtkId);
11910       }
11911       setOfVolToCheck.erase(vtkId);
11912     }
11913   }
11914
11915   // --- map of Downward faces at the boundary, inside the global volume
11916   //     map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11917   //     fill group of SMDS faces inside the volume (when several volume shapes)
11918   //     fill group of SMDS faces on the skin of the global volume (if skin)
11919
11920   std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11921   std::map<DownIdType, int, DownIdCompare> skinFaces;     // faces on the skin of the global volume --> corresponding cell
11922   std::set<int>::iterator it = setOfInsideVol.begin();
11923   for (; it != setOfInsideVol.end(); ++it)
11924   {
11925     int vtkId = *it;
11926     //MESSAGE("  vtkId " << vtkId  << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11927     int neighborsVtkIds[NBMAXNEIGHBORS];
11928     int downIds[NBMAXNEIGHBORS];
11929     unsigned char downTypes[NBMAXNEIGHBORS];
11930     int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11931     for (int n = 0; n < nbNeighbors; n++)
11932     {
11933       int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11934       if (neighborDim == 3)
11935       {
11936         if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11937         {
11938           DownIdType face(downIds[n], downTypes[n]);
11939           boundaryFaces[face] = vtkId;
11940         }
11941         // if the face between to volumes is in the mesh, get it (internal face between shapes)
11942         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11943         if (vtkFaceId >= 0)
11944         {
11945           sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11946           // find also the smds edges on this face
11947           int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11948           const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11949           const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11950           for (int i = 0; i < nbEdges; i++)
11951           {
11952             int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11953             if (vtkEdgeId >= 0)
11954               sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11955           }
11956         }
11957       }
11958       else if (neighborDim == 2) // skin of the volume
11959       {
11960         DownIdType face(downIds[n], downTypes[n]);
11961         skinFaces[face] = vtkId;
11962         int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11963         if (vtkFaceId >= 0)
11964           sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11965       }
11966     }
11967   }
11968
11969   // --- identify the edges constituting the wire of each subshape on the skin
11970   //     define polylines with the nodes of edges, equivalent to wires
11971   //     project polylines on subshapes, and partition, to get geom faces
11972
11973   std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11974   std::set<int> emptySet;
11975   emptySet.clear();
11976   std::set<int> shapeIds;
11977
11978   SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11979   while (itelem->more())
11980   {
11981     const SMDS_MeshElement *elem = itelem->next();
11982     int shapeId = elem->getshapeId();
11983     int vtkId = elem->getVtkId();
11984     if (!shapeIdToVtkIdSet.count(shapeId))
11985     {
11986       shapeIdToVtkIdSet[shapeId] = emptySet;
11987       shapeIds.insert(shapeId);
11988     }
11989     shapeIdToVtkIdSet[shapeId].insert(vtkId);
11990   }
11991
11992   std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11993   std::set<DownIdType, DownIdCompare> emptyEdges;
11994   emptyEdges.clear();
11995
11996   std::map<int, std::set<int> >::iterator itShape =  shapeIdToVtkIdSet.begin();
11997   for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11998   {
11999     int shapeId = itShape->first;
12000     MESSAGE(" --- Shape ID --- "<< shapeId);
12001     shapeIdToEdges[shapeId] = emptyEdges;
12002
12003     std::vector<int> nodesEdges;
12004
12005     std::set<int>::iterator its = itShape->second.begin();
12006     for (; its != itShape->second.end(); ++its)
12007     {
12008       int vtkId = *its;
12009       MESSAGE("     " << vtkId);
12010       int neighborsVtkIds[NBMAXNEIGHBORS];
12011       int downIds[NBMAXNEIGHBORS];
12012       unsigned char downTypes[NBMAXNEIGHBORS];
12013       int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12014       for (int n = 0; n < nbNeighbors; n++)
12015       {
12016         if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12017           continue;
12018         int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12019         const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12020         if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12021         {
12022           DownIdType edge(downIds[n], downTypes[n]);
12023           if (!shapeIdToEdges[shapeId].count(edge))
12024           {
12025             shapeIdToEdges[shapeId].insert(edge);
12026             int vtkNodeId[3];
12027             int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12028             nodesEdges.push_back(vtkNodeId[0]);
12029             nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12030             MESSAGE("       --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12031           }
12032         }
12033       }
12034     }
12035
12036     std::list<int> order;
12037     order.clear();
12038     if (nodesEdges.size() > 0)
12039     {
12040       order.push_back(nodesEdges[0]); MESSAGE("       --- back " << order.back()+1); // SMDS id = VTK id + 1;
12041       nodesEdges[0] = -1;
12042       order.push_back(nodesEdges[1]); MESSAGE("       --- back " << order.back()+1);
12043       nodesEdges[1] = -1; // do not reuse this edge
12044       bool found = true;
12045       while (found)
12046       {
12047         int nodeTofind = order.back(); // try first to push back
12048         int i = 0;
12049         for ( i = 0; i < (int)nodesEdges.size(); i++ )
12050           if (nodesEdges[i] == nodeTofind)
12051             break;
12052         if ( i == (int) nodesEdges.size() )
12053           found = false; // no follower found on back
12054         else
12055         {
12056           if (i%2) // odd ==> use the previous one
12057             if (nodesEdges[i-1] < 0)
12058               found = false;
12059             else
12060             {
12061               order.push_back(nodesEdges[i-1]); MESSAGE("       --- back " << order.back()+1);
12062               nodesEdges[i-1] = -1;
12063             }
12064           else // even ==> use the next one
12065             if (nodesEdges[i+1] < 0)
12066               found = false;
12067             else
12068             {
12069               order.push_back(nodesEdges[i+1]); MESSAGE("       --- back " << order.back()+1);
12070               nodesEdges[i+1] = -1;
12071             }
12072         }
12073         if (found)
12074           continue;
12075         // try to push front
12076         found = true;
12077         nodeTofind = order.front(); // try to push front
12078         for ( i = 0;  i < (int)nodesEdges.size(); i++ )
12079           if ( nodesEdges[i] == nodeTofind )
12080             break;
12081         if ( i == (int)nodesEdges.size() )
12082         {
12083           found = false; // no predecessor found on front
12084           continue;
12085         }
12086         if (i%2) // odd ==> use the previous one
12087           if (nodesEdges[i-1] < 0)
12088             found = false;
12089           else
12090           {
12091             order.push_front(nodesEdges[i-1]); MESSAGE("       --- front " << order.front()+1);
12092             nodesEdges[i-1] = -1;
12093           }
12094         else // even ==> use the next one
12095           if (nodesEdges[i+1] < 0)
12096             found = false;
12097           else
12098           {
12099             order.push_front(nodesEdges[i+1]); MESSAGE("       --- front " << order.front()+1);
12100             nodesEdges[i+1] = -1;
12101           }
12102       }
12103     }
12104
12105
12106     std::vector<int> nodes;
12107     nodes.push_back(shapeId);
12108     std::list<int>::iterator itl = order.begin();
12109     for (; itl != order.end(); itl++)
12110     {
12111       nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12112       MESSAGE("              ordered node " << nodes[nodes.size()-1]);
12113     }
12114     listOfListOfNodes.push_back(nodes);
12115   }
12116
12117   //     partition geom faces with blocFissure
12118   //     mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12119   //     mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12120
12121   return;
12122 }
12123
12124
12125 //================================================================================
12126 /*!
12127  * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12128  * The created 2D mesh elements based on nodes of free faces of boundary volumes
12129  * \return TRUE if operation has been completed successfully, FALSE otherwise
12130  */
12131 //================================================================================
12132
12133 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12134 {
12135   // iterates on volume elements and detect all free faces on them
12136   SMESHDS_Mesh* aMesh = GetMeshDS();
12137   if (!aMesh)
12138     return false;
12139
12140   ElemFeatures faceType( SMDSAbs_Face );
12141   int nbFree = 0, nbExisted = 0, nbCreated = 0;
12142   SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12143   while(vIt->more())
12144   {
12145     const SMDS_MeshVolume* volume = vIt->next();
12146     SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12147     vTool.SetExternalNormal();
12148     const int iQuad = volume->IsQuadratic();
12149     faceType.SetQuad( iQuad );
12150     for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12151     {
12152       if (!vTool.IsFreeFace(iface))
12153         continue;
12154       nbFree++;
12155       vector<const SMDS_MeshNode *> nodes;
12156       int nbFaceNodes = vTool.NbFaceNodes(iface);
12157       const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12158       int inode = 0;
12159       for ( ; inode < nbFaceNodes; inode += iQuad+1)
12160         nodes.push_back(faceNodes[inode]);
12161
12162       if (iQuad) // add medium nodes
12163       {
12164         for ( inode = 1; inode < nbFaceNodes; inode += 2)
12165           nodes.push_back(faceNodes[inode]);
12166         if ( nbFaceNodes == 9 ) // bi-quadratic quad
12167           nodes.push_back(faceNodes[8]);
12168       }
12169       // add new face based on volume nodes
12170       if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12171       {
12172         nbExisted++; // face already exsist
12173       }
12174       else
12175       {
12176         AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12177         nbCreated++;
12178       }
12179     }
12180   }
12181   return ( nbFree == ( nbExisted + nbCreated ));
12182 }
12183
12184 namespace
12185 {
12186   inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12187   {
12188     if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12189       return n;
12190     return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12191   }
12192 }
12193 //================================================================================
12194 /*!
12195  * \brief Creates missing boundary elements
12196  *  \param elements - elements whose boundary is to be checked
12197  *  \param dimension - defines type of boundary elements to create
12198  *  \param group - a group to store created boundary elements in
12199  *  \param targetMesh - a mesh to store created boundary elements in
12200  *  \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12201  *  \param toCopyExistingBoundary - if true, not only new but also pre-existing
12202  *                                boundary elements will be copied into the targetMesh
12203  *  \param toAddExistingBondary - if true, not only new but also pre-existing
12204  *                                boundary elements will be added into the new group
12205  *  \param aroundElements - if true, elements will be created on boundary of given
12206  *                          elements else, on boundary of the whole mesh.
12207  * \return nb of added boundary elements
12208  */
12209 //================================================================================
12210
12211 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12212                                        Bnd_Dimension           dimension,
12213                                        SMESH_Group*            group/*=0*/,
12214                                        SMESH_Mesh*             targetMesh/*=0*/,
12215                                        bool                    toCopyElements/*=false*/,
12216                                        bool                    toCopyExistingBoundary/*=false*/,
12217                                        bool                    toAddExistingBondary/*= false*/,
12218                                        bool                    aroundElements/*= false*/)
12219 {
12220   SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12221   SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12222   // hope that all elements are of the same type, do not check them all
12223   if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12224     throw SALOME_Exception(LOCALIZED("wrong element type"));
12225
12226   if ( !targetMesh )
12227     toCopyElements = toCopyExistingBoundary = false;
12228
12229   SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12230   SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12231   int nbAddedBnd = 0;
12232
12233   // editor adding present bnd elements and optionally holding elements to add to the group
12234   SMESH_MeshEditor* presentEditor;
12235   SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12236   presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12237
12238   SMESH_MesherHelper helper( *myMesh );
12239   const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12240   SMDS_VolumeTool vTool;
12241   TIDSortedElemSet avoidSet;
12242   const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12243   size_t inode;
12244
12245   typedef vector<const SMDS_MeshNode*> TConnectivity;
12246   TConnectivity tgtNodes;
12247   ElemFeatures elemKind( missType ), elemToCopy;
12248
12249   vector<const SMDS_MeshElement*> presentBndElems;
12250   vector<TConnectivity>           missingBndElems;
12251   vector<int>                     freeFacets;
12252   TConnectivity nodes, elemNodes;
12253
12254   SMDS_ElemIteratorPtr eIt;
12255   if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12256   else                  eIt = SMESHUtils::elemSetIterator( elements );
12257
12258   while (eIt->more())
12259   {
12260     const SMDS_MeshElement* elem = eIt->next();
12261     const int              iQuad = elem->IsQuadratic();
12262     elemKind.SetQuad( iQuad );
12263
12264     // ------------------------------------------------------------------------------------
12265     // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12266     // ------------------------------------------------------------------------------------
12267     presentBndElems.clear();
12268     missingBndElems.clear();
12269     freeFacets.clear(); nodes.clear(); elemNodes.clear();
12270     if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12271     {
12272       const SMDS_MeshElement* otherVol = 0;
12273       for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12274       {
12275         if ( !vTool.IsFreeFace(iface, &otherVol) &&
12276              ( !aroundElements || elements.count( otherVol )))
12277           continue;
12278         freeFacets.push_back( iface );
12279       }
12280       if ( missType == SMDSAbs_Face )
12281         vTool.SetExternalNormal();
12282       for ( size_t i = 0; i < freeFacets.size(); ++i )
12283       {
12284         int                iface = freeFacets[i];
12285         const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12286         const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12287         if ( missType == SMDSAbs_Edge ) // boundary edges
12288         {
12289           nodes.resize( 2+iQuad );
12290           for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12291           {
12292             for ( size_t j = 0; j < nodes.size(); ++j )
12293               nodes[ j ] = nn[ i+j ];
12294             if ( const SMDS_MeshElement* edge =
12295                  aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12296               presentBndElems.push_back( edge );
12297             else
12298               missingBndElems.push_back( nodes );
12299           }
12300         }
12301         else // boundary face
12302         {
12303           nodes.clear();
12304           for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12305             nodes.push_back( nn[inode] ); // add corner nodes
12306           if (iQuad)
12307             for ( inode = 1; inode < nbFaceNodes; inode += 2)
12308               nodes.push_back( nn[inode] ); // add medium nodes
12309           int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12310           if ( iCenter > 0 )
12311             nodes.push_back( vTool.GetNodes()[ iCenter ] );
12312
12313           if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12314                                                                SMDSAbs_Face, /*noMedium=*/false ))
12315             presentBndElems.push_back( f );
12316           else
12317             missingBndElems.push_back( nodes );
12318
12319           if ( targetMesh != myMesh )
12320           {
12321             // add 1D elements on face boundary to be added to a new mesh
12322             const SMDS_MeshElement* edge;
12323             for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12324             {
12325               if ( iQuad )
12326                 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12327               else
12328                 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12329               if ( edge && avoidSet.insert( edge ).second )
12330                 presentBndElems.push_back( edge );
12331             }
12332           }
12333         }
12334       }
12335     }
12336     else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12337     {
12338       avoidSet.clear(), avoidSet.insert( elem );
12339       elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12340                         SMDS_MeshElement::iterator() );
12341       elemNodes.push_back( elemNodes[0] );
12342       nodes.resize( 2 + iQuad );
12343       const int nbLinks = elem->NbCornerNodes();
12344       for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12345       {
12346         nodes[0] = elemNodes[iN];
12347         nodes[1] = elemNodes[iN+1+iQuad];
12348         if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12349           continue; // not free link
12350
12351         if ( iQuad ) nodes[2] = elemNodes[iN+1];
12352         if ( const SMDS_MeshElement* edge =
12353              aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12354           presentBndElems.push_back( edge );
12355         else
12356           missingBndElems.push_back( nodes );
12357       }
12358     }
12359
12360     // ---------------------------------
12361     // 2. Add missing boundary elements
12362     // ---------------------------------
12363     if ( targetMesh != myMesh )
12364       // instead of making a map of nodes in this mesh and targetMesh,
12365       // we create nodes with same IDs.
12366       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12367       {
12368         TConnectivity& srcNodes = missingBndElems[i];
12369         tgtNodes.resize( srcNodes.size() );
12370         for ( inode = 0; inode < srcNodes.size(); ++inode )
12371           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12372         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12373                                                                    missType,
12374                                                                    /*noMedium=*/false))
12375           continue;
12376         tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12377         ++nbAddedBnd;
12378       }
12379     else
12380       for ( size_t i = 0; i < missingBndElems.size(); ++i )
12381       {
12382         TConnectivity& nodes = missingBndElems[ i ];
12383         if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12384                                                                    missType,
12385                                                                    /*noMedium=*/false))
12386           continue;
12387         SMDS_MeshElement* newElem =
12388           tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12389         nbAddedBnd += bool( newElem );
12390
12391         // try to set a new element to a shape
12392         if ( myMesh->HasShapeToMesh() )
12393         {
12394           bool ok = true;
12395           set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12396           const size_t nbN = nodes.size() / (iQuad+1 );
12397           for ( inode = 0; inode < nbN && ok; ++inode )
12398           {
12399             pair<int, TopAbs_ShapeEnum> i_stype =
12400               helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12401             if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12402               mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12403           }
12404           if ( ok && mediumShapes.size() > 1 )
12405           {
12406             set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12407             pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12408             for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12409             {
12410               if (( ok = ( stype_i->first != stype_i_0.first )))
12411                 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12412                                         aMesh->IndexToShape( stype_i_0.second ));
12413             }
12414           }
12415           if ( ok && mediumShapes.begin()->first == missShapeType )
12416             aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12417         }
12418       }
12419
12420     // ----------------------------------
12421     // 3. Copy present boundary elements
12422     // ----------------------------------
12423     if ( toCopyExistingBoundary )
12424       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12425       {
12426         const SMDS_MeshElement* e = presentBndElems[i];
12427         tgtNodes.resize( e->NbNodes() );
12428         for ( inode = 0; inode < tgtNodes.size(); ++inode )
12429           tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12430         presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12431       }
12432     else // store present elements to add them to a group
12433       for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12434       {
12435         presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12436       }
12437
12438   } // loop on given elements
12439
12440   // ---------------------------------------------
12441   // 4. Fill group with boundary elements
12442   // ---------------------------------------------
12443   if ( group )
12444   {
12445     if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12446       for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12447         g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12448   }
12449   tgtEditor.myLastCreatedElems.clear();
12450   tgtEditor2.myLastCreatedElems.clear();
12451
12452   // -----------------------
12453   // 5. Copy given elements
12454   // -----------------------
12455   if ( toCopyElements && targetMesh != myMesh )
12456   {
12457     if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12458     else                  eIt = SMESHUtils::elemSetIterator( elements );
12459     while (eIt->more())
12460     {
12461       const SMDS_MeshElement* elem = eIt->next();
12462       tgtNodes.resize( elem->NbNodes() );
12463       for ( inode = 0; inode < tgtNodes.size(); ++inode )
12464         tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12465       tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12466
12467       tgtEditor.myLastCreatedElems.clear();
12468     }
12469   }
12470   return nbAddedBnd;
12471 }
12472
12473 //================================================================================
12474 /*!
12475  * \brief Copy node position and set \a to node on the same geometry
12476  */
12477 //================================================================================
12478
12479 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12480                                      const SMDS_MeshNode* to )
12481 {
12482   if ( !from || !to ) return;
12483
12484   SMDS_PositionPtr pos = from->GetPosition();
12485   if ( !pos || from->getshapeId() < 1 ) return;
12486
12487   switch ( pos->GetTypeOfPosition() )
12488   {
12489   case SMDS_TOP_3DSPACE: break;
12490
12491   case SMDS_TOP_FACE:
12492   {
12493     const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12494     GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12495                                 fPos->GetUParameter(), fPos->GetVParameter() );
12496     break;
12497   }
12498   case SMDS_TOP_EDGE:
12499   {
12500     // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12501     const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12502     GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12503     break;
12504   }
12505   case SMDS_TOP_VERTEX:
12506   {
12507     GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12508     break;
12509   }
12510   case SMDS_TOP_UNSPEC:
12511   default:;
12512   }
12513 }